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?
Asked
Active
Viewed 1,274 times
23
Szabolcs
- 234,956
- 30
- 623
- 1,263
Mitchell Kaplan
- 3,696
- 22
- 34
5 Answers
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
Scancan be used in such a way on held expressions. This is an important difference betweenScanandMap, that your answer highlights. – Szabolcs Jan 19 '12 at 15:23 -
-
@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:
Mapmodifies the expression,Scantakes parts of the expression and runsf[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,dinHold[]instead ofList[](i.e.,myList = Hold[a, b, c, d]). Then you can useScan[]: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
-
-
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 Jan 19 '12 at 15:21
-
Oh no, I didn't mean that! Your code is shorter and neater than mine anyway. – acl Jan 19 '12 at 15:30
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
Trace[myList:={a,b,c,d}]and ofTrace[myList={a,b,c,d}]. The former is a mistake while the latter attempts to issue a sequence ofSetassignments1=5,2=5, ...,4=5.) – whuber Jan 19 '12 at 15:01Scan[]instead ofMap[](that is,/@) for multiple assignments. – J. M.'s missing motivation Jan 19 '12 at 15:01