Вызовите переменные i [0] ... i [n], инициализируйте их все равными 1. Для увеличения на каждом шаге выполните
int j, k;
for(j = n - 1; j >= 0; j--){
initialize(i[j]);
}
while (true) {
j = 0;
while (i[j] == 2) {
doSomething(i[j]);
j++;
}
if (j < n) {
doSomething(i[j]);
} else {
break;
}
for (j = 0; j < n; j++) {
if (i[j] == 1) {
i[j] = 2;
break;
}
else {
i[j] = 1;
}
}
if (j == n) break;
for (k = j; k >= 0; k--) {
initialize(i[k]);
}
}
По сути, вы реализуете двоичный счетчик, который очищает все установленные биты меньше, чем первый бит сброса, а затем устанавливает первый бит сброса. Это запускает do dothings в том же порядке, что и данный цикл.
Вы можете делать похожие вещи с разными диапазонами, диапазоны каждого i [j] не обязательно должны быть одинаковыми.
Редактировать добавлена инициализация.
Примечание Рекурсия здесь, вероятно, излишня и несколько менее гибка, чем плоская реализация (рассмотрите возможность прерывания из внутреннего цикла.). Это проблема, которая часто возникает на практике, и не все так сложно. Мы хотим, чтобы каждый цикл выглядел как
for i = 1 to 2
do beginning stuff with i
do inner loop
do ending stuff with i
next
Если мы просто рассмотрим индексные переменные, то получим что-то вроде двоичного счетчика. Если мы посмотрим на значения индексных переменных при выполнении самого внутреннего цикла.
i j k l
1 1 1 1
1 1 1 2
1 1 2 1
реализовать счетчик легко. Если мы просто помечаем наши переменные, сначала самые внутренние, чтобы i -> i[3]
, j -> i[2]
, k -> i[1]
и l -> [0]
, то один шаг приращения состоит из
j = 0
while i[j] == 2
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
i[j] = 2
Теперь давайте сделаем вещи в конце цикла. Это легко, конец цикла происходит как раз перед тем, как мы увеличиваем. Итак, наш инкремент выглядит как
j = 0
while i[j] == 2
do ending stuff with i[j]
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
do ending stuff with i[j]
i[j] = 2
Теперь сложная часть. Сначала кажется, что мы делаем начальные вещи только после увеличения, но это имеет две проблемы.
- Он пропускает начальные приращения (те, которые происходят, когда мы устанавливаем начальные переменные в 1
- Вызывает процедуры начального приращения для всего на последнем приращении.
(это по сути одна и та же проблема)
решение первого легко, мы просто называем начальные вещи вне основного цикла.
for j = maxindex - 1 downto 0
do beginning stuff with i[j]
while true
j = 0
while i[j] == 2
do ending stuff with i[j]
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
do ending stuff with i[j]
i[j] = 2
Теперь, чтобы получить другие инициализаторы, мы делаем их после того, как увеличили количество.
for j = maxindex - 1 downto 0
do beginning stuff with i[j]
while true
j = 0
while i[j] == 2
do ending stuff with i[j]
i[j] = 1
j++
if j == maxindex
break out, we're done with all the loops
else
do ending stuff with i[j]
i[j] = 2
for k = j downto 0
do beginning stuff with i[k]
Это не требует дополнительных затрат (стек вызовов и т. Д.) Для версии с вложенным циклом. Это облегчает аборт из глубины гнезда. Это немного сложно, но я бы удивился, если бы это было сложнее, чем рекурсивная версия с явным стеком. De gustibus non disputandum est.