9
$\begingroup$

Lets say I define a Module, that internally use iterating in a Table or Do, say something like this:

fun[n_] := Module[{var1, var2},
    Table[2i+3, {i, n}]
];

Is it good practice to include i among the local variables? Can I get intro trouble if I do not? For example, I suspect that

i=0;
Print[Dynamic[i]];
Do[fun[k]; i++;, {k, 1, 100}];

makes i flicker in the dynamic output, due to i not being local.

$\endgroup$
15
$\begingroup$

In general, it is good practice to include i among the local variables. Table does not localize its variable (or, as some say, it only localizes the value but not the variable).

It is relatively safe to leave i unlocalized when variables only have numeric values, like in fun. But the same is not true when variables can have symbolic values, e.g.:

fun2[x_] := Table[x + i, {i, 3}]

At first it seems to work:

ClearAll[a, b, i]

fun2[a]
(* {1 + a, 2 + a, 3 + a} *)

fun2[b]
(* {1 + b, 2 + b, 3 + b} *)

... but unexpected results are obtained if the caller just happens to use the same symbol as the iteration variable:

fun2[i]
(* {2, 4, 6} *)

As observed in the question, the reason for this behaviour is that i is not being localized by Table. Table notionally uses Block to temporarily clear the value of i in the outer scope, assign it successively to each iteration value, and then finally restore the original outer value.

Sum can produce similar surprises:

sum[x_] := Sum[x + i, {i, 3}]
sum /@ {a, b, i}
(* {6 + 3 a, 6 + 3 b, 12} *)

As can Do. Such lack of localization can indeed cause unexpected variable value changes ("flickering") in the context of Dynamic, Monitor, etc.

If we localize i, then we get no surprises:

fun3[x_] := Module[{i}, Table[x + i, {i, 3}]]

fun3[a]
(* {1+a, 2+a, 3+a} *)

fun3[b]
(* {1+b, 2+b, 3+b} *)

fun3[i]
(* {1+i, 2+i, 3+i} *)

sum3[x_] := Module[{i}, Sum[x + i, {i, 3}]]
sum3 /@ {a, b, i}
(* {6 + 3 a, 6 + 3 b, 6 + 3 i} *)

Table and Sum are still re-using i from the outer scope, but now the outer scope is the one introduced by Module. i in the global scope is now left untouched and we see unsurprising results.

In simple examples like these, it is usually easy to see by inspection whether the lack of localization by Table will be problematic. But in more complex functions, that analysis can be more difficult. Therefore, I would suggest that it is good defensive programming practice to routinely localize iteration variables. Especially when writing library code.

For a detailed analysis of the subtle differences between Module and Block (and With), see What are the use cases for different scoping constructs?.

Why do iterators work like this?

The scoping behaviour that we see here is a common idiom across all Mathematica functions that use iterators (e.g. Table, Sum, Plot, Plot3D, Do, For, Integrate, RecurrenceTable and many more). Why does it behave this way? Would it not be safer if all of these constructs used Module for localization instead of Block?

The answer is that the present behaviour is more convenient for interactive operation, particularly when one is engaged in the kinds of symbolic manipulations that motivated the creation of Mathematica in the first place. The Mathematica tutorials are full of examples like this:

expr = i^2;

Table[expr, {i, 1, 10}]

(* {1, 4, 9, 16, 25, 36, 49, 64, 81, 100} *)

If the iterator variable i were localized as if by Module, then the result would be:

(* {i^2, i^2, i^2, i^2, i^2, i^2, i^2, i^2, i^2, i^2} *)

Less than useful for an ad hoc interactive session.

The design decision could have gone either way, but I have found that in such cases the decision made often favours interactive use over library code implementation. This general principle does mean that we must take greater care when writing re-usable ("library") code.

$\endgroup$
  • $\begingroup$ Ah, yes, this is what I suspected. Thanks! $\endgroup$ – Per Alexandersson Sep 6 '15 at 12:47
2
$\begingroup$

The answer to your first question in "no". The i in Table is automatically localized for you by Mathematica.

The answer to your question about the flickering of i in the dynamic expression is the it caused by the high-frequecy updating of i by i++. It would still flicker even it i were not a global variable as long as all the instances of i were in the same scope.

BTW, you don't need Print to wrap Dynamic[x] to see it update.

$\endgroup$
  • $\begingroup$ That's strange, I just ran some code, in a similar fashion, where I have a dynamic i that flickers, with the same value, and increments slowly, so I am sure that the flickering is not due to speedy incrementation. $\endgroup$ – Per Alexandersson Sep 6 '15 at 3:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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