Choosing Adequate Loop-Constructs
While there are many loop-like constructs I think there are 5 that every Mathematica programmer should know, 4 of them come as pairs which either do return the result of each iteration or don't, but otherwise share syntax:
- Map - Scan
- Table - Do
- While
As a general rule it is a good idea to always use the most simple construct that provides the functionality you need: A large majority of loop constructs do iterate over a given list/array and Map
or Scan
would be the best choice for these. They do away with any kind of indexing and iteration issues. In some cases it is useful/necessary to have a handle to a loop counter, this is what Table
or Do
provide. If you need a more flexible iteration scheme, then there is While
, which is providing the most general case of a loop that I can think of. Using "more advanced" loop-like constructs in my opinion is only worth considering when they exactly match your use case and make that basically a one-liner. If you need tricks to map them to your use case that will often result in rather "write only" type of code. I would rather urge users to have a look at level specifications for Map
/Scan
and additional arguments for Table
/Do
(to specify nesting loops and step sizes) and the Sow
/Reap
mechanism to collect results from a arbitrary loop.
Why Not to Use For
-Loops
While many of these other loop-like constructs in Mathematica have their use cases, I would strongly discourage the use of For
loops: I have never used one in any Mathematica code I ever wrote. As mentioned by Leonid in one of the comments in most cases (I'd guess about 95% of the time when I see one) it is Do
that is really needed. In the other maybe 5% it is While
that is needed. The cases where one might think a For
loop would be necessary can basically be reduced to two cases:
- You need a loop counter: you'd better use
Do
or Table
, their syntax is more concise, it is much easier to see what they actually do and they localize loop counters.
- You need a more complex iteration scheme than what
Do
or Table
provide: I prefer to use While
for these cases since it is a lot easier to read and For
doesn't add anything but unnecessary complexity.
I consequently use Do
/Table
for case 1 and While
for case 2 (and only for case 2) so when I see Do
or While
in my code I readily get some extra information about the nature of the loop.
Readability of Code
A While
loop is much easier to read than a For
loop: There are basically three parts of code within such a loop: initialization, condition-check and body. In a While
loop everything that happens before the While
is initialization, the first argument of the While
is the condition-check and the second is the body. In a For
loop you can distribute the initialization between code before the For
loop and the first argument, and you can arbitrarily distribute the body part between the 3rd and 4th argument. Consequently it will always need extra thinking about what will be evaluated in which order (see Can this be written well, without loops? for an example: can you see the order of evaluation at first glance? I only realized that the loop counters were incremented after the nested loops after rewriting with While
).
Technical Details and Performance
Concerning all the technical details I think For
doesn't provide anything that Do
and While
don't: they return Null, they can be compiled (I would guess that both Do
and While
should even outperform For
in compiled code), and you can use Break
and Continue
within them.
It is not only friendly to a possible reader to use the most simple construct that covers your need. It is just as well easier for Mathematica to understand your code, which lets it do a better job when optimizing the code (of course that holds just as well for other constructs than loops). Auto-compilation of Table is one case, here is another example:
res1 = Table[Table[i*j, {i, 100}], {j, 100}];
res2 = Table[i*j, {i, 100}, {j, 100}];
looks like not much difference and it's easy to check that these in fact create the same result, except for a small detail:
Developer`PackedArrayQ[res1]
Developer`PackedArrayQ[res2]
(* ==> False *)
(* ==> True *)
this can result in tremendous differences in the runtime and memory efficiency of this construct and all the following code which works with the result.
EDIT
There is one more important difference between a typical For
loop and its Do
correspondance which becomes clear when using floating point counters, compare e.g.:
h = 10.^-5;
Do[If[i >= 1 - h, Print[InputForm@i]], {i, 0, 1, h}]
to:
h = 10.^-5;
For[i = 0, i <= 1, i += h, If[i >= 1 - h, Print[InputForm@i]]]
Of course it really depends on what you want to achieve, but I think in most use cases you would actually prefer the equidistant division that Do
seems to do over the accumulated errors of For
which make it on my 64bit Mathematica 9 on Windows 7 "miss" the upper limit. It is again an example where Mathematica can act smarter when the construct you use is the simplest possible and thus more clear. If accumulating the errors would be what you actually want to happen, then again a While
would still do the same thing with less ambiguities (again it took me a second try to realize that the increment in For
is done after evaluating the body, something that is immediately clear in the While
code and could accordingly be changed if so desired):
h = 10.^-5;
i = 0;
While[i <= 1, If[i >= 1 - h, Print[InputForm@i]]; i += h;]
Label
andGoto
: figuring out what a FORTRAN code was doing. $\endgroup$Scan
as well. It is as close as one gets to loops from a functional side, but more rigid (iteration order is prescribed, and there are no iteration variables). $\endgroup$