Cicero would have written his entire code in a single for-loop.
seen from United States

seen from United States
seen from Japan
seen from India
seen from Netherlands
seen from United States
seen from Egypt

seen from United States

seen from South Africa

seen from Iraq
seen from United States

seen from United States

seen from Iraq
seen from United States
seen from United States
seen from China
seen from United States

seen from France
seen from Vietnam

seen from United States
Cicero would have written his entire code in a single for-loop.
Unlooping the re-loops
“You can do anything with stacks and iteration that you can do with recursion.” — Steve McConnell, Code Complete
For almost as long as we’ve been talking about recursion, and especially recently because of our recent project, the question has been on my mind: Why recursion, when, like Steve McConnell says, loops (and stacks – and I presume queues) can do the same thing? I mean of course, recursion is fantastic simply because of the self-reference, but a little less philosophically, what’s so great about recursion anyway?
Well, it’s obvious why recursion beats a for loop any day: not only does a for loop require a specific range of items or indices to look at, but you can’t break out of a for loop before the end of that range (nicely – I maintain that break is nearly as bad as goto – cf. https://xkcd.com/292/).
But, you may then ask, what about while loops? They continue on condition of an expression, just like recursive programs do, and even if recursive functions can take multiple arguments, while loops can simply store data outside of the loop – no function calls needed.
But I think the important points here are space and clarity. For programs that need to build a progressively larger dataset (like our assignment’s DFS and BFS PuzzleNode builders), recursion makes each function call – each child – be operated on separately from the rest of its family, and all extraneous data is discarded once that recursive call returns out. A while loop storing data outside of the loop doesn’t have this luxury. And, perhaps even more crucially, a recursive function can simply be easier to understand, especially for programs that have clearly defined modules that function independently, and only their results are assembled. Programmers are humans too, so if it’s easier – why not go for it? After all, like I said from day one: laziness is key!
For-Loops
The standard C-style for-loop (also available in languages like C++ and Java) looks like this:
for (initialization; termination_condition; step) { ... } /*for*/
For example, if you had an array of 10 integers:
int arr[10];
you might print them out as follows:
for (unsigned int i = 0; i < 10; ++i) { printf("arr[%d] = %d\n", i, arr[i]); } /*for*/
A couple of notes:
Each of initialization, termination_condition and step is optional; if the termination condition is omitted, it is replaced with true (loop forever).
In addition to the termination condition, the loop can also be exited by a break statement within it.
As a matter of style, I don’t like to have both a termination_condition and embedded break statements; I just use one or the other. For example, the above loop could also be written:
for (unsigned int i = 0;) { if (i == 10) break; printf("arr[%d] = %d\n", i, arr[i]); ++i; } /*for*/
There is no advantage to doing this in this case, but it does become important when the break does not naturally go at the beginning or end of the loop, or you need more than one break within the loop
For example, consider trying to print the elements in the reverse order:
for (unsigned int i = 9; i >= 0; --i) /* won’t work! */ { printf("arr[%d] = %d\n", i, arr[i]); } /*for*/
Why won’t this work? OK, it would have worked if i had been declared int instead of unsigned int. I declared it unsigned here because it doesn’t need to be signed. Also in some cases you need the extra positive range of unsigned integers.
However, an embedded break can come to the rescue:
for (unsigned int i = 10;;) { if (i == 0) break; --i; printf("arr[%d] = %d\n", i, arr[i]); } /*for*/
The termination test could be moved to the end, unless the 10 isn’t always a fixed 10, but could dynamically vary, and sometimes be 0. Then leaving it where it is is the best idea.