Qiang Li
Qiang Li

Reputation: 10855

Understanding module argument modification in Mathematica

If I do the following in Mathematica

f[l_] := Module[{}, l[[1]] = Append[l[[1]], 3]; l]
f[{{}, 3}]

I get an error:

Set::setps: "{{},3} in the part assignment is not a symbol. "

Even l={{}, 3};f[l] gets the same error. But I can do f[l_] := Module[{}, {Append[l[[1]], 3],l[[2]]}] or l = {{}, 3}; l[[1]] = Append[l[[1]], 3]; l.

What is your explanation?

Upvotes: 2

Views: 2509

Answers (3)

Mr.Wizard
Mr.Wizard

Reputation: 24336

There are multiple problems here:

  1. Attempting Part assignment on a non-Symbol, just as the error message states.

  2. Attempting to manipulate a named replacement object as though it were a symbol.

The replacement that takes place in this construct:

f[x_] := head[x, 2, 3]

Is analogous to that of With:

With[{x = something}, head[x, 2, 3]]

That is, the substitution is made directly and before evaluation, such that the function Head never even sees an object x. Look what happens with this:

ClearAll[f,x]
x = 5;
f[x_] := (x = x+2; x)

f[x]
During evaluation of In[8]:= Set::setraw: Cannot assign to raw object 5. >>

Out[]= 5

This evaluates as: (5 = 5+2; 5) so not only is assignment to 5 impossible, but all instances of x that appear in the right hand side of := are replaced with the value of x when it is fed to f. Consider what happens if we try to bypass the assignment problem by using a function with side effects:

ClearAll[f, x, incrementX]

incrementX[] := (x += 2)
x = 3;
incrementX[];
x
5

So our incrementX function is working. But now we try:

f[x_] := (incrementX[]; x)

f[x] 
5

incrementX did not fail:

x
7

Rather, the the value of x was 5 at the time of evaluation of f[x] and therefore that is returned.


What does work?

What options do we have for things related to what you are attempting? There are several.

1. Use a Hold attribute

We can set a Hold attribute such as HoldFirst or HoldAll on the function, so that we may pass the symbol name to RHS functions, rather than only its value.

ClearAll[heldF]
SetAttributes[heldF, HoldAll]

x = {1, 2, 3};

heldF[x_] := (x[[1]] = 7; x)

heldF[x]
x
<pre>{7, 2, 3}</pre>
<pre>{7, 2, 3}</pre>

We see that both the global value of x, and the x expression returned by heldF are changed. Note that heldF must be given a Symbol as an argument otherwise you are again attempting {1, 2, 3}[[1]] = 7.

2. Use a temporary Symbol

As Arnoud Buzing shows, we can also use a temporary Symbol in Module.

ClearAll[proxyF]

x = {1, 2, 3};

proxyF[x_] := Module[{proxy = x}, proxy[[1]] = 7; proxy]

proxyF[x]
proxyF[{1, 2, 3}]
x
{7, 2, 3}
{7, 2, 3}
{1, 2, 3}

3. Use ReplacePart

We can also avoid symbols completely and just use ReplacePart:

ClearAll[directF]

x = {1, 2, 3};

directF[x_] := ReplacePart[x, 1 -> 7]

directF[x]
x
{7, 2, 3}
{1, 2, 3}

This can be used for modifications rather than outright replacements as well:

ClearAll[f]

f[l_] := ReplacePart[l, 1 :> l[[1]] ~Append~ 3]

f[{{}, 3}]
{{3}, 3}

Upvotes: 6

Arnoud Buzing
Arnoud Buzing

Reputation: 15423

If you do want to use Part in your Module, you may want to consider using a temporary variable:

f[l_List] := Module[{t = l}, t[[1]] = Pi; t]

And:

In[] := f[{1, 2, 3}]

Out[] = {Pi, 2, 3}

Upvotes: 1

acl
acl

Reputation: 6520

Try

f[{{}, 3}] // Trace

and you see that the value of l is inserted into the l[[1]] = Append[l[[1]], 3] bit before evaluation. So mma is attempting to evaluate this: {{}, 3}[[1]] = {3}

This may do something like you want

ClearAll[f];
f[l_] := Module[{},
  Append[l[[1]], 3]~Join~Rest[l]
  ]

(the idea is to avoid assigning to parts of l, since l will be evaluated before the assignment is attempted)

Upvotes: 1

Related Questions