Services & Resources / Wolfram Forums / MathGroup Archive
-----

MathGroup Archive 2010

[Date Index] [Thread Index] [Author Index]

Search the Archive

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.
>
>
>



  • Prev by Date: Re: How to write reports and books in Mathematica
  • Next by Date: Re: How to write reports and books in Mathematica
  • Previous by thread: Re: How to Explain This Behavior of Simple Procedures?
  • Next by thread: Re: How to Explain This Behavior of Simple Procedures?