Re: How to Explain This Behavior of Simple Procedures?
- To: mathgroup at smc.vnet.net
- Subject: [mg109639] Re: How to Explain This Behavior of Simple Procedures?
- From: Leonid Shifrin <lshifr at gmail.com>
- Date: Sun, 9 May 2010 07:50:57 -0400 (EDT)
Hi James, The behavior you observed in not buggy and also not unexpected when you know the mechanics of Mathematica evaluation. You are obviously trying to implement pass-by-reference in your function calls. To do that in Mathematica, you need to give your functions Hold* - attributes, covering those arguments that you want to pass by reference. If you don't, the arguments get evaluated before they are used in the body of your function, and then instead of variables your function will work with (often immutable) copies of their content (their values). Attempt to modify these copies will often cause error messages. Your first call is fine because the local variable does not have a value initially, and because you only call <f> once. But try this for instance: In[1] = ClearAll[f, g]; f[lhs_, rhs_] := lhs = rhs; g[rhs_] := Module[{localVar}, f[localVar, rhs]; f[localVar, rhs]; Print["g: localVar=", localVar];]; g[44]; Set::setraw: Cannot assign to raw object 44. >> g: localVar=44 And, sure enough, you get an error since by the time we call <f> second time, your <localVar> already has a value. Since <f> did not hold its arguments, localVar evaluated to that value, and the second call was then completely equivalent to calling f[44,44]. Fixing the behavior in the first case is easy: just set a HoldFirst attribute for <f>: SetAttributes[f, HoldFirst];, and it will work. Fixing the second example is harder, because when you use assignment of the form expr[[1]] =rhs, (so called Part assignment), Mathematica expects <expr> to be a Symbol whose value (technically called OwnValue) is a list (or, more generally, normal expression which has a first part). In your case, you have not a symbol but an expression {localVar}, so this won't work even when we fix the problem with Hold*-attributes. There are several ways to fix this, but which one is best depends on what exactly you wish to accomplish. Generally pass-by-reference is discouraged in Mathematica programming (there are exceptions), and in any case to implement it in less trivial cases (like your second one) you will need to understand the Mathematica evaluation process and know how to work with held expressions. I will show just one example: a function that takes a list of values and a sequence of variables of the same length, and assigns values to variables: ClearAll[setMany]; SetAttributes[setMany, HoldRest]; setMany[values_List, vars___] /; Length[values] == Length[Hold[vars]] := Set @@ Hold[{vars}, values] This uses the special form of simultaneous assignment available in Mathematica, like {a,b,c} = {1,2,3}. For example: Clear[a, b, c]; setMany[{1, 2, 3}, a, b, c]; {a, b, c} {1, 2, 3} Here is how you could use it: Clear[g]; g[rhs_] := Module[{localVar1, localVar2}, setMany[{rhs, rhs^2}, localVar1, localVar2]; Print["g: localVar1=", localVar1]; Print["g: localVar2=", localVar2]]; g[3] g: localVar1=3 g: localVar2=9 You may want to look at http://www.mathprogramming-intro.org/book/node173.html and http://www.mathprogramming-intro.org/book/node207.html where I dwell on the topic of parameter-passing in Mathematica, and at http://www.mathprogramming-intro.org/book/node205.html where I try to explain what Hold attributes are. I however do not explain how to work with held expressions. Working with held expressions is explained well in the books of Roman Maeder (Programming in Mathematica), and David Wagner (Power programming in Mathematica: the kernel). The latter is unfortunately out of print. Hope this helps. Regards, Leonid On Sat, May 8, 2010 at 4:06 AM, James Stein <mathgroup at stein.org> wrote: > I think the scanty procedures below show unexpected, perhaps buggy, > behavior. > > These two procedures work as expected: > f assigns a value to a variable passed in by g: > > *Input:* > > ClearAll [f, g]; > f [lhs_, rhs_] := lhs = rhs; > g[rhs_] := Module[{localVar}, > f [localVar, rhs]; > Print["g: localVar=", localVar]; > ]; > g[44]; > > > *(printed) Output:* > > g: localVar= 44 > > > But this method, where f receives the variable differently, fails: > > > *Input:* > > ClearAll [f, g]; > f [lhs_List, rhs_] := lhs[[1]] = rhs; > g[rhs_] := Module[{localVar}, > f [{localVar}, rhs]; > Print["g: localVar=", localVar]; > ]; > g[44]; > > > *(printed) Output:* > > g: localVar=localVar$313085 > > > In addition, there is an error message: > > Set::setps : > > (localVar$313085} in the part assignment is not a symbol. > > which is curious because: > > Head[localVar$313085] returns Symbol. > > > My motivation here is to allow a procedure to return one or more subsidiary > results in variables provided by the caller. > > >