Some people advise against the use of For
loops in Mathematica. Why? Should I heed this advice? What is wrong with For
? What should I use instead?
-
3$\begingroup$ This comes up so often, I wanted to have something to link to. As always on StackExchange, other answers are welcome. $\endgroup$– SzabolcsCommented Jan 2, 2017 at 14:40
-
3$\begingroup$ As, still, someone not that fluent in Mathematica, and a longtime programmer (mostly C), this was the most useful question/answer I've read - and I have read a bunch of these! Thanks to both the questioner and the responder. $\endgroup$– Jack AdamsCommented Jan 3, 2017 at 21:45
-
3$\begingroup$ Related (my own self Q&A): Alternatives to procedural loops and iterating over lists in Mathematica: "Explicit loops are often counterproductive in Mathematica, not only taking more keystrokes, but also more execution time. They are also, in my opinion, more prone to mistakes." $\endgroup$– Mr.WizardCommented Jan 5, 2017 at 9:30
4 Answers
If you are new to Mathematica, and were directed to this post, first see if you can use Table
to solve your problem.
I have often told people, especially beginners, to avoid using For
in favour of Do
. The following is my personal opinion on why using For
is harmful when learning Mathematica. If you are a seasoned Mathematica user, you won't find much to learn here. My biggest argument against For
is that it hinders learning by encouraging error-prone, hard to read, and slow code.
For
mimics the syntax of the for loop of C-like languages. Many beginners coming from such languages will look for a "for loop" when they start using Mathematica. Unfortunately, For
gives them lots of ways to shoot themselves in the foot, while providing virtually no benefits over alternatives such as Do
. Settling on For
also tends to delay beginners in discovering more Mathematica-like programming paradigms, such as list-based and functional programming (Table
, Map
, etc.)
I want to make it clear at the beginning that the following arguments are not about functional vs procedural programming. Functional programming is usually the better choice in Mathematica, but procedural programming is also clearly needed in many situations. I will simply argue that when we do need a procedural loop, For
is nearly always the worst choice. Use Do
or While
instead.
Use Do
instead of For
The typical use case of For
is iterating over an integer range. Do
will do the same thing better.
Do
is more concise, thus both more readable and easier to write without mistakes. Compare the following:For[i=1, i <= n, i++, f[i]; g[i]; h[i] ]
Do[ f[i]; g[i]; h[i], {i, n} ]
In
For
we need to use both commas (,
) and semicolons (;
) in a way that is almost, but not quite, the opposite of how they are used in C-like languages. This alone is a big source of beginner confusion and mistakes (possibly due to muscle memory).,
and;
are visually similar so it is hard to spot the mistake.For
does not localize the iteratori
. A safeFor
needs explicit localization:Module[{i}, For[i=1, i <= n, i++, ...] ]
A common mistake is to overwrite the value of a global
i
, possibly defined in an earlier input cell. At other timesi
is used as a symbolic variable elsewhere, andFor
will inconveniently assign a value to it.In
Do
,i
is a local variable, so we do not need to worry about these things.C-like languages typically use 0-based indexing. Mathematica uses 1-based indexing.
for
-loops are typically written to loop through0..n-1
instead of1..n
, which is usually the more convenient range in Mathematica. Notice the differences betweenFor[i=0, i < n, i++, ...]
and
For[i=1, i <= n, i++, ...]
We must pay attention not only to the starting value of
i
, but also<
vs<=
in the second argument ofFor
. Getting this wrong is a common mistake, and again it is hard to spot visually.In C-like languages the for loop is often used to loop through the elements of an array. The literal translation to Mathematica looks like
For[i=1, i <= n, i++, doSomething[array[[i]]] ]
Do
makes this much simpler and clearer:Do[doSomething[elem], {elem, array}]
Do
makes it easy to use multiple iterators:Do[..., {i, n}, {j, m}]
The same requires a nested
For
loop which doubles the readability problems.
Transitioning to more Mathematica-like paradigms
A common beginner-written program that we see here on StackExchange collects values in a loop like this:
list = {};
For[i=1, i <= n, ++i,
list = Append[list, i^2]
]
This is of course not only complicated, but also slow ($O(n^2)$ complexity instead of $O(n)$). The better way is to use Table
:
Table[i^2, {i, n}]
Table
and Do
have analogous syntaxes and their documentation pages reference each other. Starting out with Do
makes the transition to Table
natural. Moving from Table
to Map
and other typical functional or vectorized (Range[n]^2
) constructs is then only a small step. Settling on For
as "the standard looping construct" leaves beginners stuck with bad habits.
Another very common question on StackExchange is how to parallelize a For
loop. There is no parallel for in Mathematica, but there is a ParallelDo
and more importantly a ParallelTable
. The answer is almost always: design the computation so that separate steps of the iteration do not access the same variable. In other words: just use Table
.
More general versions of For
For
is of course in some ways more flexible than Do
. It can express a broader range of iteration schemes. If you need something like this, I suggest just using While
instead.
When we see for
, we usually expect either a simple iteration through an integer range or through an array. Doing something else, such as modifying the value of the iterator in the loop body is unexpected, therefore confusing. Using While
signals that anything can happen in the loop body, so the readers of the code will watch out for such things.
When is For
appropriate?
There are some cases when For
is useful. The main example is translating code from other languages. It is convenient to be able to translate analogous for loops, and not have to think about what may be broken by immediately translating to a Do
or a Table
(e.g. does the loop modify the iterator in the body?). Once the translated code works fine, it can be rewritten gradually.
There are existing questions on this, which also discuss other cases:
Summary
The problem with For
is that it hinders learning and makes it very easy for beginners to introduce mistakes into their code.
If you are new to Mathematica, my advice is to forget that For
exists, at least for a while. You can always accomplish the very same things with Do
and While
—use them instead. Very often you will be able to replace Do
with a Table
or even a vectorized expressions. This will help you learn to write effective Mathematica code faster.
If you are unsure about a use of For
, then ask yourself: do I see a reason why For
is clearly better here than Do
or While
? If not, don't use it. If yes, you may have found one of the rare good use cases.
-
5$\begingroup$ @Feyre It is, in several ways. And the jump from
Table
to vectorized computation is quite straightforward—another reason to useDo
, which then leads toTable
, which then leads to the paradigm you show. But there is only so much space in one post. I wrote this up so I can link to it when people ask why they are told to avoidFor
. $\endgroup$– SzabolcsCommented Jan 2, 2017 at 15:05 -
2$\begingroup$ +1, but one nit. I've never understood how
Table
is considered more inline with a "functional" paradigm thanFor
. There are obvious utilities with usingTable
/Do
overFor
, but other languages have used similar forms for years, usually expressed asForEach
and c++ has gotten in on the act via the ranged for. $\endgroup$– rcollyerCommented Jan 2, 2017 at 16:37 -
2$\begingroup$ Avoid
For
,Do
is better. AvoidDo
,Table
is better. AvoidTable
,Range^2
is better. When is it good enough? $\endgroup$ Commented Jan 2, 2017 at 18:45 -
3$\begingroup$ @DepressedDaniel when our computations become fast enough to solve the most basic problems in Ramsey Theory ;) let's start with $R(5,5)$ and work from there... $\endgroup$ Commented Jan 2, 2017 at 19:05
-
4$\begingroup$ "
For
does not localize the iterator i" -- that alone for me is a sufficient argument to avoidFor
. All the rest are good points, but mostly about style / readability whereas this one actually breaks the functional pattern (I strive to have as few assignments as possible in general) and may lead to hard-to-find bugs (as opposed to simple typo's that Mathematica will tell you about). $\endgroup$ Commented Jan 3, 2017 at 13:17
Illustration of the timings required to compute the squares i^2
from i=1
to i=10^n
for n=1, 2, ..., 7
with the use of For
, While
, Do
, Table
, and Range
.
for = Table[
Module[{i},
For[i = 1, i <= 10^n, i++, i^2] // AbsoluteTiming // First
]
, {n, 1, 7}]
while = Table[
Module[{i},
i = 1; While[i <= 10^n, i^2; i++] // AbsoluteTiming // First
]
, {n, 1, 7}]
do = Table[Do[i^2, {i, 10^n}] // AbsoluteTiming // First, {n, 1, 7}]
table = Table[
Table[i^2, {i, 10^n}]; // AbsoluteTiming // First, {n, 1, 7}]
range = Table[Range[10^n]^2; // AbsoluteTiming // First, {n, 1, 7}]
(By the way, look how concise are the codes of Do
, Table
and Range
compared to For
and While
.)
The timings for 10^7
squares (i.e., n=7
):
Last /@ {for, while, do, table, range}
{7.32907, 8.23668, 2.44558, 0.132735, 0.036395}
And a plot (vertical axis in log-scale):
ListLogPlot[{for, while, do, table, range}, Frame -> True,
PlotRange -> All, Joined -> True, ImageSize -> 400,
FrameLabel -> {"n", "Log[AbsoluteTiming] (sec)"},
PlotLabels -> {"For", "While", "Do", "Table", "Range"}]
Do
is about $3\times$ faster than For/While
; for this particular application, one could (and should) employ Table/Range
, which are two orders of magnitude faster than For
.
-
2$\begingroup$ Not sure that's a fair comparison, because: (1)
Do
, for example, throws away everything calculated (returningNull
) whereasTable
, for example, actually returns the list of squares; and (2) calculatingi^2
is relatively trivial and misleadingly magnifies the effect of the looping construct used. $\endgroup$– murrayCommented Jan 2, 2017 at 16:51 -
$\begingroup$ @murray I've made it a community wiki; feel free to improve. $\endgroup$– corey979Commented Jan 2, 2017 at 16:53
-
1$\begingroup$ @murray In general, you are right: one of the things the benchmark shows is the performance of the looping construct itself, as
i^2
is so fast. In more typical uses it is the speed of looping that is negligible compared to the loop body. But there's more here:Table
is fast because of auto-compilation. Auto-compilation is possible because of the functional nature ofTable
: each element of the table is computed independently of the others and usually without side effects. $\endgroup$– SzabolcsCommented Jan 3, 2017 at 11:32 -
1$\begingroup$ @murray In contrast, each evaluation done by
Do
will have side effects, otherwise it would be pointless. Usually, that side effect is changing the same global variable (e.g. accumulating a sum), which makes the evaluations non-independent. ThusDo
does not auto-compile. Similarly,Table
is immediately parallelizable whileDo
isn't. My original answer wasn't simply about performance. There's much more to it: readability, manageability, etc. But I think it is true that usingFor
can significantly hinder performance. Of course this applies to Mathematica only, not other languages. $\endgroup$– SzabolcsCommented Jan 3, 2017 at 11:32 -
2$\begingroup$ +1 for actual science, even if a microbenchmark-y one $\endgroup$– user11734Commented Jan 3, 2017 at 14:40
The functional paradigm, exemplified by this code:
Map[(#^2) &, Range[10^7]] // AbsoluteTiming
will usually result in the fastest execution because it takes advantage of the architecture of the machine. Both the CPU and the memory are optimized for sequential access, so when you pass a function over a list of data to transform that data, the code stays in one place, taking advantage of locality (no code-cache misses), and the data is accessed as one continuous stream of bytes. The above line of code takes 0.281 seconds to complete on my computer, while the line below ran for well over an hour and only produced a list 1,190,218 elements long:
out = {};
For[i = 1, i <= 10^7, ++i, AppendTo[out, i^2]] // AbsoluteTiming
-
7$\begingroup$ The fastest way is in fact using array arithmetic (i.e. "vectorization"):
Range[10^7]^2
.Map
needs to evaluate Mathematica code for each element, which is slower than handling the entire array with a low-level implementation. To be precise,Map
will auto-compile#^2&
, which gives a considerable speedup. But it is still not competitive with parallelized array arithmetic that makes use of SIMD instructions. $\endgroup$– SzabolcsCommented Apr 5, 2017 at 16:59 -
2$\begingroup$ But generally you are correct. If this were not something that can be expressed with basic vector arithmetic, then
Map
would be a good way. It is only purely numerical operations on packed arrays that will benefit for vectorization. $\endgroup$– SzabolcsCommented Apr 5, 2017 at 17:31
A problem that arose in a recent Q&A, which is solved by Outer
(not yet mentioned here), was
- How to iterate a function over the cartesian product of lists and store the values generated?
For the sake of illustration, take two lists:
a = {"a", "b", "c"};
b = {1, 2, 3, 4};
We want to apply a function f
to each ordered pair of elements from a
and b
. A for-loop way would be as follows:
m = Length[a];
n = Length[b];
table = ConstantArray[0, {m, n}];
For[i = 1, i <= m, i++,
For[j = 1, j <= n, j++,
table[[i, j]] = f[a[[i]], b[[j]]]
]
];
table
(* see output below *)
The following does the same:
table = Outer[f, a, b]
(* {{f["a", 1], f["a", 2], f["a", 3], f["a", 4]}, {f["b", 1], f["b", 2], f["b", 3], f["b", 4]}, {f["c", 1], f["c", 2], f["c", 3], f["c", 4]}} *)
For a product three lists, use
Outer[f, a, b, c]
And so on.
The same can be done with Table
:
table = Table[f[ai, bj, {ai, a}, {bj, b}] (* less efficient than Outer *)
table = Table[f[a[[i]], b[[j]], {i, m}, {j, n}] (* much less efficient *)
Like Outer
, Table
can be extended to higher dimensional products.