0
$\begingroup$

I have some code that I am porting from VBA to Mathematica. The original code processes data on an Excel spreadsheet. In Mathematica, I find it convenient to replicate the Excel spreadsheet data using global variables. I created a function that inadvertently depends on how the function is called. The original code is rather complicated. Here is simple code that has the same type of behavior.

testFn:=(
iter=1;
While[iter<=iMax,
u=f+1;
f=u^2;
Print["iter=",iter,"; u=",u,"; f=",f];
iter++;
];
{u,f}
);

The code is evaluated by 4 input statements:

f=0;u=0;iMax=3;
testFn
u
f

If all four input statements are evaluated at one time (select all 4 and shift-enter), the results for the last two statements are u=5 and f=25; however, if the statements are evaluated one at a time, the results for the last two statements are u = 458330 and f = 210066388900. (This is the correct result when you evaluate testFn twice in succession.)

I have found two ways to get the correct results every time - whether the data is evaluated one line at a time, or all lines together. These are shown below

Put testFn on the first line:

f=0;u=0;iMax=3;testFn
u
f

Suppress the output of testFn with a semicolon:

f=0;u=0;iMax=3;
testFn;
u
f

An even simpler function shows the same behavior:

testFn2:=(
  u=f+1;
  f=u^2;
  {u,f}
)

As before, the following statements give different results depending on whether they are evaluated one line at a time, or as a group:

u=0;f=0;
testFn2
u
f

As before, putting the functional call "testFn2" on the first line, or suppressing the output of testFn2 with a semicolon solves the problem.

How do I modify the function so that the results do not depend on how the functions are evaluated (one at a time or in a group of statements)?

What's going on?

Thank you for your help!

MAK (Mathematica 10.0.2.0 on Windows 7)

$\endgroup$
2
$\begingroup$

The simple issue you are having is that u and f retain their values from the previous evaluation of testFn. You either need to initialise them each time in the body of the function, or (better) create local variables so that your result doesn't depend on how many times you evaluate testFn.

I wouldn't ordinarily recommend the use of loops to do anything like this in Mathematica. For purposes of illustration, I will use a very simple amendment to your code that localises u and f. (You will need to evaluate Clear[testFn, u, f] first to avoid problems, or use different names.)

testFn[u_, f_, maxiter_] := 
  Module[{iter = 1, uu = u, ff = f}, 
   While[iter <= maxiter, uu = ff + 1; ff = uu^2; 
    Print["iter=", iter, "; u=", uu, "; f=", ff]; iter++;]; {uu, ff}];

You can see that I have created local variables in the first (list) argument to Module, which initialise the iterator iter as well as pass the original parameter values of u and f to local variables uu and ff. These local variables are then operated on and returned.

You call the function like this:

testFn[0, 0, 3] 

The important point here is that you will not be able to call the values of u and f separately by typing and evaluating those letters, but neither will you have problems with the output depending on how many times you have evaluated your function.

In fact, the way you have defined this function, you don't even need an initial value of u - it's determined by the initial value of f. (Is that your intention?) You can check this by doing something like Table[testFn[i, 0, 3], {i, 4}].

So an even simpler version is:

testFn[f_, maxiter_] := 
  Module[{iter = 1, uu, ff = f}, 
   While[iter <= maxiter, uu = ff + 1; ff = uu^2; 
    Print["iter=", iter, "; u=", uu, "; f=", ff]; iter++;]; {uu, ff}];

Have a look at this question to learn more about scoping constructs and local variables.

While I'm answering this, I thought you might find it useful to have a look at some non-loop versions of this using Nest and NestList:

version2[f_, maxiter_] := {1, 0} + 
   Take[NestList[(# + 1)^2 &, f, maxiter], -2]

version3[f_, maxiter_] := 
  With[{ff = Nest[(# + 1)^2 &, f, maxiter]}, {Sqrt[ff], ff}]

I'm sure there are other, cleaner ways as well, but lunchbreak is over now.

$\endgroup$
  • $\begingroup$ Thank you for your suggestions. $\endgroup$ – MAK Jan 12 '16 at 13:38
  • $\begingroup$ Thank you for your suggestions - and your time on your lunchbreak! I typically avoid global variables, but in this particular case I would like u and f to be global variables, if possible. Note that the code that I inserted shows the same problems as my code, but is much simpler. If it is not possible to use global variables, I will certainly do something like your first suggestion. (The use of nested lists is very elegant, but harder to read for the average C programmer.) I still do not understand why the results depend on whether I evaluate one statement at a time, or many all together $\endgroup$ – MAK Jan 12 '16 at 13:52

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.