60
$\begingroup$

To my mind the only reason for the existence of For[] loops in Mathematica is to allow new users with some experience in procedural programming languages to write simple Mathematica programs. The real problem is that sometimes such a user do not dive deeper and as a consequence he has 1) ugly and unreliable code and 2) a belief that "Mathematica has a C-like syntax".

It is hard for me to imagine a situation when For[] cannot be effectively and more cleanly replaced by Table, Map, anything else or Do at least. Am I wrong? Should we really recommend beginners to avoid For[] loops?


SUMMARY

There are actually two great answers. The first answer is the accepted one and the second answer is this question. I wish I could accept it as well.

$\endgroup$
5
  • $\begingroup$ No reason for them except simply because people want to use them. They're slow. Mathematica has labels and Goto statements as well. I don't recommend them. $\endgroup$
    – Searke
    Commented Feb 22, 2012 at 18:45
  • 4
    $\begingroup$ @Searke I've had one occasion where I used Label and Goto: figuring out what a FORTRAN code was doing. $\endgroup$
    – rcollyer
    Commented Feb 22, 2012 at 18:54
  • $\begingroup$ Oops, I didn't know For was bad...! The advantage, as I thought, over functional structures, was that you didn't accumulate temporary results like you do with Table. For example, in each iteration, I'd create a graphic, and write it to disk. If I used Table to collect images, Mathematica crashes after 10 minutes... I promise to read about Do tomorrow... :) $\endgroup$
    – cormullion
    Commented Feb 22, 2012 at 20:10
  • 1
    $\begingroup$ @cormullion Have a look at 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$ Commented Feb 22, 2012 at 20:14
  • 1
    $\begingroup$ Perhaps if the algorithm you're implementing lends itself very well to loops your code will be most readable if you use loops. $\endgroup$
    – Andrew
    Commented Feb 29, 2012 at 23:52

5 Answers 5

46
$\begingroup$

To my mind, there are at least two cases when For loops are ok:

  • Inside Compile, or in code which is being written with Compile in mind
  • When your inner loops are vectorized or made efficient by some other means, so that each iteration of the For loop is sufficiently intensive computationally

Many efficient algorithms are procedural by nature and gain their efficiency by side effects and local mutations. When those algorithms contain nested loops, what matters the most is to speed up innermost loop(s). While we mostly tend to move away from For loops, I have no problem with a For loop being an outer loop in a program, as long as innermost loops are optimized. Also, For loops are more flexible than Map or Scan because you can use Break and Continue, and generally are not forced to iterate over all of the elements in a list in a prescribed order.

That said, I think we should recommend beginners to avoid For loops, just because this would allow them to change their mindset and get to the better understanding of Mathematica programming sooner. I'd say, it is ok to occasionally use For loops for experienced users, but beginners would be better off avoiding them entirely, until they get more experience with the language.

$\endgroup$
11
  • 6
    $\begingroup$ I am not sure I see where For loops are preferable to Do, which localizes the iterator (unless you are using For in a more complicated way than For[i = 0, i < 4, i++, Print[i]], but that is probably rare) $\endgroup$
    – acl
    Commented Feb 22, 2012 at 18:58
  • 5
    $\begingroup$ @acl I agree, that in many cases where For is used, Do is what you really need. Some cases when you may want For (or While, which is its close relative), is when you either need more complex iteration, ot the number of iterations may not be known in advance (like e.g. in this code), or when you want to use Return to return from the entire function containing a loop (using Return from Do will only break from Do). $\endgroup$ Commented Feb 22, 2012 at 19:03
  • 1
    $\begingroup$ @Eli Lansey In uncompiled code, Do is typically more efficient that While, and Map can be much more efficient still, since it can auto-compile when the function being mapped is compilable. Note that Map is also different in that it procuduces the resulting list, while Do or While (or For) return Null. In compiled code, Do is still the fastest, and in my experience While comes second, and Map is somewhat slower IIRC. $\endgroup$ Commented Feb 22, 2012 at 19:48
  • 1
    $\begingroup$ @Artes ..flies in the face of extreme generality of the term-rewriting engine of Mathematica. A better strategy IMO would be to introduce more strongly typed sub-languages within Mathematica, so that programs written in those sub-languages could be compiled. This is the direction which I think has a great potential. In a sense, Compile defines one such sub-language, but I am sure it should be possible to define other sub-languages, and write custom compilers from those to C or whatever target language you want. Integrating those with efficient Mathematica low-level structures is hard though. $\endgroup$ Commented Feb 23, 2012 at 13:26
  • 1
    $\begingroup$ @LeonidShifrin in reply to [A better strategy IMO ...] I agree. Expression optimisation (without compilation to low level instructions) can also benefit from such restrictions IMO. But compilation to low level instructions is also very interesting. I find myself sticking only the functions that have analogues in SymbolicC whenever I write code that is intended to be compiled and most of the time translation to actual SymbolicC is quite straightforward and leads to great speedups. I hope that there will be more tools for such a workflow. By the way I also agree with your answer :). $\endgroup$ Commented May 18, 2014 at 12:48
36
$\begingroup$

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:

  1. Map - Scan
  2. Table - Do
  3. 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:

  1. 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.
  2. 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;]
$\endgroup$
6
  • 2
    $\begingroup$ +1. I actually consider For and While to be very close to each other, because both are inherited from C (in terms of syntax), where the difference between them is more a matter of personal preference (you can leave out some parts of for and have while, like for(;condition;){}). So when I was speaking of For loops, I meant both For and While (I should have stated that in my answer), and admittedly I also often prefer While to For. $\endgroup$ Commented Feb 23, 2012 at 11:49
  • 1
    $\begingroup$ @Leonid: you are of course right, and it might be worth mentioning that one can leave out parts in a Mathematica For as well (use Null if you don't want to see the syntax highlight warnings). But that is my criticism: For seems to add complexity without actually provide new features compared to While. Thus my preference for While: it doesn't add unnecessary load to my brain :-) $\endgroup$ Commented Feb 23, 2012 at 13:09
  • $\begingroup$ Yes, I agree. All your points are very valid. I think our answers are complementary to each other. $\endgroup$ Commented Feb 23, 2012 at 13:17
  • 2
    $\begingroup$ A little remark: readability of code is rather subjective. $\endgroup$ Commented Mar 30, 2014 at 11:46
  • 2
    $\begingroup$ @TZakrevskiy: of course code readability is to a large part subjective and strongly influenced by prior experience. But there are certainly several established rules which are widely accepted and which are beyond pure personal taste. Reduced complexity seems to be a very decent and objective measure of code readability to me which was why I mentioned it in my answer. $\endgroup$ Commented Mar 30, 2014 at 16:14
5
$\begingroup$

I'd argue that For should be used when you want to iterate through a list of options and insure that they are not run in parallel.

To give a silly example, Table[CelebrateBirthday[i],{i,0,18}] is equivalent to For[i=0,i<19,i++,CelebrateBirthday[i]]

However, a later programmer might try and performance tune by changing the first example into a ParallelTable, causing the birthdays to be celebrated in a non-deterministic order. The For loop, on the other hand, signals to any future coders that the loop wasn't designed to be run in parallel and that care should be taken before changing the order of operations.

I'd tell a newbie that, if you want your code to run slowly, use For. Otherwise, use Table or Map.

$\endgroup$
4
  • 1
    $\begingroup$ also, Table attempts to autocompile $\endgroup$
    – acl
    Commented Feb 22, 2012 at 19:49
  • 2
    $\begingroup$ Those would be equivalent if Table was Do $\endgroup$
    – Rojo
    Commented Feb 22, 2012 at 22:46
  • 7
    $\begingroup$ If you want a reader of your code to know that a loop is not to be parallelized I think a comment would server much better than the use of a suboptimal programming construct which, in principle, could be target of parallelization just as others... $\endgroup$ Commented Feb 23, 2012 at 11:06
  • $\begingroup$ @Albert I agree that a comment would be the better options, but a belt and suspenders approach would argue that it makes sense to do both. Also, while a programmer can sit down and try and turn a For loop into parallel code, already has a built in ParallelTable, ParallelMap, and ParallelDo, but no ParallelFor. If a future programmer wants to parallelize the For loop, she's at least going to be forced to read the code first. $\endgroup$
    – rprospero
    Commented Feb 23, 2012 at 17:24
4
$\begingroup$

I have used a For (or more usually While) loop in conjunction with Sow and Reap when I am unsure of the number of iterations I will be requiring. I'll iterate until a condition is met, and Reap all the solutions.

Note: There might be a better/faster way that I'm unaware of to do this same sort of thing.

$\endgroup$
1
  • $\begingroup$ I use For in the same circumstances, provided I don't know how many iterations I'm going to need and I also need an iteration counter. In some cases, it may be possible to work out the number of iterations outside the loop, in which case Table[] becomes useful, but if that determination is sufficiently complex and introduces the possibility of (programmer) error, Table[] may be an easier and potentially more robust solution. $\endgroup$ Commented Feb 29, 2012 at 14:43
4
$\begingroup$

Nobody mentioned use of Throw, Catch for very versatile exiting from loops.

$\endgroup$
4
  • 4
    $\begingroup$ I actually would not recommend this technique for standard situations. If you have nested loops, you can wrap Module around them end exit with Return[..., Module], or just Return. Exceptions are for exceptional circumstances IMO. Besides, people tend to abuse untagged exceptions, which are pretty bad (I consider Throw and Catch without tags a language defect), so this would just lead to lots of trouble IMO. Finally, exceptions cause an overhead, and I'd expect that they can not be compiled and will slow down the code if we use it (code) inside Compile (although I did not try it). $\endgroup$ Commented Feb 22, 2012 at 20:19
  • $\begingroup$ In my RootSearch package I call an iterative algorithm which looks for a root where there is likely to be a root. That algorithm calls a function which calls a function and so on. A few functions deep it may be discovered that the function whose roots we a looking fo is not defined at the new approximation of the root. When that happens I use Throw to give up on the iterative algorithm in that neighborhood. I don't know if you call that an exception. Also, we can use Check to take certain action when we compute 0/0 etc. $\endgroup$
    – Ted Ersek
    Commented Feb 22, 2012 at 22:06
  • $\begingroup$ This sounds like a more involved flow of control than simply nested loops, in which case exceptions (Throw) may be justified. I expressed my views on this in this answer, albeit in the context of error-checking. One thing I am absolutely opposed to is using Throw as Throw[expr] rather than Throw[expr,tag]. I think it is totally acceptable to use Throw-Catch to implement non-trivial flow of control, but this should be reserved for cases when nothing else helps. $\endgroup$ Commented Feb 22, 2012 at 22:26
  • $\begingroup$ Also, while it is hard for me to make conclusions without looking at the code, but normally, even for involved algorithms, I almost never saw a situation when the logic could not be implemented with local gotos (Break, Continue, etc) and really cried for Throw-Catch. I still think that these belong more to the software engineering side of the language than algorithmic, and should be used as such most of the time. This is just my opinion, of course, you don't have to agree with it. $\endgroup$ Commented Feb 22, 2012 at 22:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.