23
$\begingroup$

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?

$\endgroup$
3
  • $\begingroup$ 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.) $\endgroup$
    – whuber
    Commented Jan 19, 2012 at 15:01
  • 4
    $\begingroup$ FYI: It's a bit better to use Scan[] instead of Map[] (that is, /@) for multiple assignments. $\endgroup$ Commented Jan 19, 2012 at 15:01
  • $\begingroup$ 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. $\endgroup$ Commented Jan 22, 2012 at 22:34

5 Answers 5

28
$\begingroup$

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]]
$\endgroup$
9
  • 6
    $\begingroup$ +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. $\endgroup$
    – Szabolcs
    Commented Jan 19, 2012 at 15:23
  • $\begingroup$ @Szabolcs I second that. Did not realize it either. $\endgroup$ Commented Jan 19, 2012 at 15:59
  • $\begingroup$ @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. $\endgroup$ Commented Jan 19, 2012 at 23:07
  • 4
    $\begingroup$ @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 $\endgroup$
    – Szabolcs
    Commented Jan 20, 2012 at 11:56
  • 1
    $\begingroup$ +1, a slightly shorter variant: Scan[Function[p, p = 5, HoldAll], Hold[a, b, c, d]] $\endgroup$
    – WReach
    Commented Jan 21, 2012 at 19:26
20
$\begingroup$

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}
$\endgroup$
2
  • $\begingroup$ Late answers rarely get the attention they deserve. (This one wasn't even very late.) Long overdue +1. $\endgroup$
    – Mr.Wizard
    Commented Aug 18, 2013 at 2:49
  • 1
    $\begingroup$ @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 $\endgroup$
    – Ali Hashmi
    Commented Feb 4, 2017 at 13:39
18
$\begingroup$

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}
$\endgroup$
3
  • $\begingroup$ Very cool, I was writing up something very similar. +1 $\endgroup$
    – acl
    Commented Jan 19, 2012 at 15:19
  • 2
    $\begingroup$ @acl Thanks. Didn't intend to steal your answer. But it seems like there will be enough cool questions for all of us. $\endgroup$ Commented Jan 19, 2012 at 15:21
  • $\begingroup$ Oh no, I didn't mean that! Your code is shorter and neater than mine anyway. $\endgroup$
    – acl
    Commented Jan 19, 2012 at 15:30
14
$\begingroup$

You could use

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

Function[x, x=5, {HoldAll}] /@ myList // ReleaseHold
$\endgroup$
10
$\begingroup$

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