23

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?

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
Mitchell Kaplan
  • 3,696
  • 22
  • 34
  • 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 Jan 19 '12 at 15:01
  • 4
    FYI: It's a bit better to use Scan[] instead of Map[] (that is, /@) for multiple assignments. – J. M.'s missing motivation Jan 19 '12 at 15:01
  • 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 Jan 22 '12 at 22:34

5 Answers5

27

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.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
  • 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 Jan 19 '12 at 15:23
  • @Szabolcs I second that. Did not realize it either. – Leonid Shifrin Jan 19 '12 at 15:59
  • @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 Jan 19 '12 at 23:07
  • @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 missing motivation Jan 20 '12 at 01:28
  • 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 Jan 20 '12 at 11:56
  • @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 Jan 20 '12 at 22:29
  • 1
    +1, a slightly shorter variant: Scan[Function[p, p = 5, HoldAll], Hold[a, b, c, d]] – WReach Jan 21 '12 at 19:26
  • 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 Jan 22 '12 at 22:39
  • @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 missing motivation Jan 23 '12 at 03:07
20

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
  • 68,832
  • 4
  • 164
  • 269
  • Late answers rarely get the attention they deserve. (This one wasn't even very late.) Long overdue +1. – Mr.Wizard Aug 18 '13 at 02:49
  • 1
    @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 Feb 04 '17 at 13:39
18

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}
Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
14

You could use

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

Function[x, x=5, {HoldAll}] /@ myList // ReleaseHold
Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
10

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]

Thread also works:

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

Thread[Hold[{a, b, c, d}, 5]] /. Hold -> Set
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371