Call the variables i[0] ... i[n] initialize them all to 1. To increment at each step do
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]);
}
}
Essentially you are implementing a binary counter clearing all the set bits less than the first clear bit, then setting the first clear bit. This runs the do somethings in the same order as the given loop.
You can do similar things with different ranges, the ranges of each i[j] need not even be the same.
Edit added initialization.
Note Recursion is probably overkill here, and is somewhat less flexible than the flat implementation (consider aborting from the inner loop.). This is a problem that comes up often in practice, and is not all that complicated. What we want to do is to have each loop look like
for i = 1 to 2
do beginning stuff with i
do inner loop
do ending stuff with i
next
If we just consider the index variables we have what looks like a binary counter. If we look at the values of the index variables when we perform the innermost loop.
i j k l
1 1 1 1
1 1 1 2
1 1 2 1
implementing a counter is easy. If we just label our variables, innermost first, so that i -> i[3]
, j -> i[2]
, k -> i[1]
and l -> [0]
then a single incrementing step consists of
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
now, let us do the stuff at the end of the loop. This is easy, the end of the loop happens just before we increment. So our incrementer looks like
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
Now, the tricky part. It at first appears that we do the beginning stuff just after incrementing, but this has two problems.
- It misses the initial increments (the ones that happen when we set the initial variables to 1
- It calls inital increment routines for everything on the final increment.
(these are essentially the same problem)
the solution to the first is easy, we just call beginning stuff outside the main loop.
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
Now to get the other initializers we do them after we have incremented the count.
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]
This takes no overhead (call stack, etc) over the nested loop version. It makes it easy to abort from deep in the nest. It is a bit tricky, but I'd be surprised if it is more complicated than the recursive version with an explicit stack. De gustibus non disputandum est.