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
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 simply
100as simply as
y = 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
.