## Reassign values to symbols

22

11

I've got a situation where I have, say 4 symbols, a, b, c and d. This is a simplification of the issue I've been working with. Let's say I assign numeric values to these symbols:a=1; b=2; c=3; d=4. I now create a list: myList:={a,b,c,d}. I'd like to be able to say: ( # = 5 ) & /@ myList to assign the value 5 to a, b, c and d. The code works as long as a-d are unassigned. Is there a way to do this, or am I trying to abuse the language?

A primary aim of mine is to be able to make a bunch of reassignments using map, rather than writing all of the assignment statements. Another aim is to use meaningful names for the symbols and then being able to retrieve the name of the symbol, along with any values I assign to it. Typically the symbols will represent lists, I just used integers to make it easier to write out an example. – Mitchell Kaplan – 2012-01-22T22:34:14.627

What exactly is your aim in general? (To understand your example, look at the output of Trace[myList:={a,b,c,d}] and of Trace[myList={a,b,c,d}]. The former is a mistake while the latter attempts to issue a sequence of Set assignments 1=5, 2=5, ..., 4=5.) – whuber – 2012-01-19T15:01:07.213

4FYI: It's a bit better to use Scan[] instead of Map[] (that is, /@) for multiple assignments. – J. M.'s ennui – 2012-01-19T15:01:28.010

26

This seems to work:

a = 1; b = 2; c = 3; d = 4;
Scan[Function[p, p = 5, HoldAll], Hold[a, b, c, d]]

Now, try evaluating {a, b, c, d}.

Here's the version with slots:

Scan[Function[Null, # = 5, HoldAll], Hold[a, b, c, d]]

J.M.: I tried your example and it worked as written. However if I assign a list, say mList={a,b,c,d}, and try to substitute mList it doesn't work. However, all I've done is copied what you had. Since I hadn't used Scan before and am not all that comfortable with Hold, I didn't really understand it. I'll spend some more time with those functions so I see what you're doing. Thanks for the help. – Mitchell Kaplan – 2012-01-22T22:39:44.493

@Mitchell, what you'll want to do here is to wrap a,b,c,d in Hold[] instead of List[] (i.e., myList = Hold[a, b, c, d]). Then you can use Scan[]: Scan[Function[p, p = 5, HoldAll], myList] (i.e., what WReach did). – J. M.'s ennui – 2012-01-23T03:07:59.827

6+1, Though it's very logical, I didn't realize Scan can be used in such a way on held expressions. This is an important difference between Scan and Map, that your answer highlights. – Szabolcs – 2012-01-19T15:23:10.670

@Szabolcs I second that. Did not realize it either. – Leonid Shifrin – 2012-01-19T15:59:20.943

@J.M +1 out of curiosity how did you figure out this behaviour. I've just had a scan (pun intended) of the documentation and I don't think you would know that this would work for this example from the description there. – Mike Honeychurch – 2012-01-19T23:07:39.187

@Mike: I've always used Scan[] for automated multiple assignments. I experimented a bit to see how it acts in conjunction with held expressions, and it worked out nicely here. – J. M.'s ennui – 2012-01-20T01:28:19.753

4@MikeHoneychurch Now that I see that it works, it seems very logical: Map modifies the expression, Scan takes parts of the expression and runs f[part] for each. Is our way of thinking too constrained maybe? This is why I love this site and interaction with others :-) You always learn something new – Szabolcs – 2012-01-20T11:56:41.190

@Szabolcs yes I think sometimes you get accustomed to using certain functions and ways of doing things. I actually needed to do this a few weeks ago. Forgot what i did now but it was nowhere near as nice and straight forward as using Scan. Always good to learn things. – Mike Honeychurch – 2012-01-20T22:29:43.820

1+1, a slightly shorter variant: Scan[Function[p, p = 5, HoldAll], Hold[a, b, c, d]] – WReach – 2012-01-21T19:26:43.930

18

We can define a new "variable container" that can be used to assign the same value to multiple variables:

ClearAll[vars]
SetAttributes[vars, HoldAll]
vars /: s:(_vars = _) := CompoundExpression @@ Thread[Unevaluated@s, vars, 1]

It is used like this:

In[4]:= ClearAll[a, b, c, d]
vars[a, b, c, d] = 5
Out[5]= 5

In[6]:= {a, b, c, d}
Out[6]= {5, 5, 5, 5}

In[7]:=  vars[a, b, c, d] = 66
Out[7]=  66

In[8]:=  {a, b, c, d}
Out[8]=  {66, 66, 66, 66}

In[9]:=  vec = {1, 2, 3, 4};
vars[vec[[2]], vec[[4]]] = 999
Out[10]= 999

In[11]:= vec
Out[11]= {1, 999, 3, 999}

@WReach. This is a brilliant approach. +1 for the approach. Alternatively, while playing with your code I found that one can also make different assignments to the symbols by using this: var /: patt : (_var = _var) := Thread[Unevaluated@patt, var] /. var :> List – Ali Hashmi – 2017-02-04T13:39:10.697

Late answers rarely get the attention they deserve. (This one wasn't even very late.) Long overdue +1. – Mr.Wizard – 2013-08-18T02:49:05.517

17

If you insist on working with your list where you assemble variables, this will do it:

setValues  =
Function[{vlist, val},
OwnValues[vlist] /. (_ :> vars_) :>
Replace[Unevaluated@vars, var_ :> (var = val), {1}],
HoldFirst];

For example:

In[73]:= myList:={a,b,c,d}
In[74]:= a=1;b=2;c=3;d=4;

In[77]:= setValues[myList,5];

In[78]:= myList
Out[78]= {5,5,5,5}

Very cool, I was writing up something very similar. +1 – acl – 2012-01-19T15:19:48.093

2@acl Thanks. Didn't intend to steal your answer. But it seems like there will be enough cool questions for all of us. – Leonid Shifrin – 2012-01-19T15:21:57.260

Oh no, I didn't mean that! Your code is shorter and neater than mine anyway. – acl – 2012-01-19T15:30:30.343

13

You could use

myList = Hold[a,b,c,d]

Function[x, x=5, {HoldAll}] /@ myList // ReleaseHold

9

One could use Outer for this purpose:

{a, b, c, d} = {1, 2, 3, 4};

Outer[Set, Hold[a, b, c, d], Hold[5], 1] /. Hold -> List

or:

{a, b, c, d} = {1, 2, 3, 4};

Outer[Set, Unevaluated[{a, b, c, d}], {5}, 1]