Order of evaluation - does Mathematica evaluate variables before or after substitution?

Try Trace:

    Trace[y = x^2;
 x = 10;
 z = y + y^2 + y^3]

The result is enter image description here

From this result it looks like the second variant takes place.

Have fun!


Yes, x^2 is computed three times. One way to see it is this:

In[1]:= On[]                                                                                                                                     

On::trace: On[] --> Null.

In[2]:= y = x^2                                                                                                                                  

                 2      2
Set::trace: y = x  --> x .

         2
Out[2]= x

In[3]:= x = 10                                                                                                                                   

Set::trace: x = 10 --> 10.

Out[3]= 10

In[4]:= z = y + y^2 + y^3                                                                                                                        

                 2
y::trace: y --> x .

x::trace: x --> 10.

               2       2
Power::trace: x  --> 10 .

                2
Power::trace: 10  --> 100.

                 2
y::trace: y --> x .

x::trace: x --> 10.

               2       2
Power::trace: x  --> 10 .

                2
Power::trace: 10  --> 100.

               2        2
Power::trace: y  --> 100 .

                 2
Power::trace: 100  --> 10000.

                 2
y::trace: y --> x .

x::trace: x --> 10.

               2       2
Power::trace: x  --> 10 .

                2
Power::trace: 10  --> 100.

               3        3
Power::trace: y  --> 100 .

                 3
Power::trace: 100  --> 1000000.

                  2    3
Plus::trace: y + y  + y  --> 100 + 10000 + 1000000.

Plus::trace: 100 + 10000 + 1000000 --> 1010100.

                     2    3
Set::trace: z = y + y  + y  --> z = 1010100.

Set::trace: z = 1010100 --> 1010100.

Out[4]= 1010100

In[5]:= Off[]     

I find this easier to follow than Trace but it produces too much junk in recent versions when run in the front end, so I had to run it in a terminal.

This observation does not imply that the result of y -> x^2 -> 10^2 -> 100 is not cached! (I don't know if it is.)

You can re-assign y form x^2 to simply100as simply asy = y` in this case. Better, you could make it a function and use memoization to cache the result:

Clear[y]
y[x_] := y[x] = x^2

Yes, it seems the order can matter, at least when using SetDelayed (:=), which is common for functions. In that case, if you swapped the first two lines, it would indeed go faster.

One way to see this is with a very inefficient function, which calculates Fibonacci numbers the long way:

slowFib[n_] := If[n <= 2, 1, slowFib[n - 1] + slowFib[n - 2]];

On my machine, it takes about 3.2 seconds to calculate the 29th Fibonacci number this way:

slowFib[29] // AbsoluteTiming
(*  {3.23395, 514229}  *)

Here is a slow calculation, with the same ordering as your example code. It takes 9.7 seconds to run the last line, because it is running slowFib 3 times to calculate y1:

y1 = slowFib1[29];
slowFib1 = slowFib;
z1 = y1 + y1 + y1 // AbsoluteTiming
(*  {2.*10^-6, slowFib1[29]}
    {1.*10^-6, slowFib}
    {9.65774, 1542687}  *)

Compare this to a "fast" version with the first 2 lines swapped. It calculates the value of slowFib[29] just once, when it assigns the value to y2, so it only does so once, and takes 3.3 seconds total:

slowFib2 = slowFib ) // AbsoluteTiming
(y2 = slowFib2[29]) // AbsoluteTiming
(z2 = y2 + y2 + y2) // AbsoluteTiming
(*  {1.*10^-6, slowFib}
    {3.28709, 514229}
    {4.*10^-6, 1542687}  *)

What's going on here is that Mathematica only evaluates slowFib[29] when it is called. In the fast code (vesion 2), it gets called once when defining y2. In the slow code (version 1), it gets called thrice, when defining z1.

Tags:

Evaluation