0
$\begingroup$

Is it possible to use a list as a variable, i.e., to pass it by reference to a function?

In particular, I have a two-dimensional array and a function to get one element specified by its position:

s = {{1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}};
valueAtPos[positionX_, positionY_, lattice_List] :=  
  Flatten[Take[lattice, {positionY, positionY}, {positionX, positionX}]]

This works just fine, it does exactly what I want.

Now, what I'm actually aiming at is "flipping" the value at a certain position, i.e. $-1 \rightarrow 1$ and vice versa. I tried it with a function and a fixed list and it worked, but since I have more than one list, I want to pass the actual list not a copy as an argument as well. So I tried this:

flip[posX_, posY_, lattice_List] := 
  Module[{latticeLocal = lattice, x = posX, y = posY},
    latticeLocal = 
      ReplacePart[latticeLocal, {y,x} -> -valueAtPos[x, y, latticeLocal]] //. 
       {{1} :> 1, {-1} :> -1};]

If I run, for example, flip[1, 1, s], it should change s to

s = {{-1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}}.

but it doesn't; s is left untouched, exactly the same as before. If I run

s = ReplacePart[s,{1,1} -> -valueAtPos[1, 1, latticeLocal]] //. {{1} :> 1, {-1} :> -1};

it does exactly what it's supposed to do.

My question is: How can I pass a list, not a copy of the list, to a function so the list I pass will really get modified.

(I tried using the list like a normal argument of a function and I got errors. Modules, at least, didn't give me errors)

Edit

Nasser told me to delete the semicolon in flip and to assign the output of the function to my original list. That works, of course, but is not quite what I wanted, so I am going to clarify this here.

I want to use the flip in another function. And I not only want to run the function, I also want to use its output. Look at this:

algorithm[steps_, T_, lattice_List] := 
  Module[{latticeLocal = lattice},
    For[i = 0, i < steps, i++, 
      {
        (* definig some variables inside the for-loop *)
        If[(* some expression *), 
          latticeLocal = flip[posX, posY, latticeLocal],
          0] 
      }];]

This doesn't work. I need to use the output of flip in the for-loop, and I don't have an idea how to do that.

$\endgroup$
5
  • $\begingroup$ remove the ; at the end of your function, and assign the result to s, like this: !Mathematica graphics $\endgroup$
    – Nasser
    Commented Nov 16, 2013 at 20:27
  • $\begingroup$ Well, if I run flip without the $;$, the output is right, but it doesn't change the list. I guess changing the list per se is not possible, at least not with Module[]. Is there another way? $\endgroup$
    – Jo Mo
    Commented Nov 16, 2013 at 20:34
  • $\begingroup$ Mathematica passes things by value, not by reference, like in say Fortran. Yes, you can force by reference in M, but this requires extra setting. I'll post an answer if that what you want, but it is best to assign the result to s $\endgroup$
    – Nasser
    Commented Nov 16, 2013 at 20:45
  • $\begingroup$ I just saw your edit. I do not know if this was after I answered or before. But I assume now it is all clear? Or is there still a problem? $\endgroup$
    – Nasser
    Commented Nov 16, 2013 at 23:21
  • $\begingroup$ And I just now saw this comment. Actually, I still have huge problems in understanding the answers you linked me to, specifically the part about upvalues. I just don't see how to use this for my problem, so I'll have to read up a bit. Thanks for showing me where my main problem apparently is! $\endgroup$
    – Jo Mo
    Commented Nov 17, 2013 at 10:52

1 Answer 1

2
$\begingroup$

update 11/17/13:

Added this example from Wagner book, on the danger of using HoldAll. This is a good reason why one should stick to default pass-by-value in Mathematica. Here is the example:

ClearAll[p, inc, double, x];
SetAttributes[inc, HoldAll];
SetAttributes[double, HoldFirst];
inc[x_] := x = x + 1;
double[x_] := x + x
p = 4;
inc[p];   (* this makes p=5 now *)
double[p] (* this makes p=10 now *)

gives as expected :

(*  10  *)

Now changing the call to emulate call by reference, and look what happens now:

ClearAll[p, inc, double, x];
SetAttributes[inc, HoldAll];
SetAttributes[double, HoldFirst];
inc[x_] := x = x + 1;
double[x_] := x + x
p = 4;
double[inc[p]]  (* what do you think p will be after this? *)

Gives

(* 11 *)

Clearly what happens, as the book says, is that in the second case, inc[p] was evaluated twice, and not one time as one would expect.

Hence inc[p]; double[p]; gave different result to double[inc[p]].

Original answer

First method: Unevaluated

Wrap the argument you want to pass by reference with Unevaluated

flip[posX_, posY_, lattice_] := 
  Module[{x = posX, y = posY}, 
   lattice = ReplacePart[lattice, {y, x} -> -valueAtPos[x, y, lattice]] //. {{1} :> 
       1, {-1} :> -1}
   ];
valueAtPos[positionX_, positionY_, lattice_List] := 
 Flatten[Take[lattice, {positionY, positionY}, {positionX, positionX}]]
s = {{1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}};

Now

flip[1, 1, Unevaluated[s]];
s
(*  {{-1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}}  *)

Disadvantages: You lose the ability to add type checking (as in adding lattice_List in the formal parameter, must write it as lattice only )

Second method: HoldAll

Make the function itself HoldAll

ClearAll[flip];
flip[posX_, posY_, lattice_] := 
  Module[{x = posX, y = posY}, 
   lattice = 
    ReplacePart[lattice, {y, x} -> -valueAtPos[x, y, lattice]] //. {{1} :> 
       1, {-1} :> -1}
   ];
Attributes[flip] = HoldAll;
valueAtPos[positionX_, positionY_, lattice_List] := 
 Flatten[Take[lattice, {positionY, positionY}, {positionX, positionX}]]
s = {{1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}};

now

flip[1, 1, s];
s
(* {{-1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}} *)

Third method HoldFirst

Move the one argument to be passed by reference to first argument and use HoldFirst

ClearAll[flip];
flip[lattice_, posX_, posY_] := 
  Module[{x = posX, y = posY}, 
   lattice = 
    ReplacePart[
      lattice, {y, x} -> -valueAtPos[x, y, lattice]] //. {{1} :> 
       1, {-1} :> -1}
   ];
Attributes[flip] = HoldFirst;
valueAtPos[positionX_, positionY_, lattice_List] := 
 Flatten[Take[lattice, {positionY, positionY}, {positionX, positionX}]]
s = {{1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}};

gives

flip[s, 1, 1];
s
(* {{-1, 1, 1, -1}, {1, 1, -1, -1}, {-1, -1, 1, -1}, {-1, 1, -1, 1}} *)

But I think, in the spirit of Mathematica programming and functional programming, is that one should not do these things. i.e. functions should not have side-effects. To modify something, write newValue = foo[ oldValue ] and pass things by value, which is the default.

see also passing-large-list-by-reference and does-pass-by-value-affect-the-performance-of-function-calls

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.