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?
$\begingroup$ $\endgroup$
3 5 Answers
$\begingroup$ $\endgroup$
9 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]] - 6$\begingroup$ +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. $\endgroup$Szabolcs– Szabolcs2012-01-19 15:23:10 +00:00Commented Jan 19, 2012 at 15:23 - $\begingroup$ @Szabolcs I second that. Did not realize it either. $\endgroup$Leonid Shifrin– Leonid Shifrin2012-01-19 15:59:20 +00:00Commented 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$Mike Honeychurch– Mike Honeychurch2012-01-19 23:07:39 +00:00Commented Jan 19, 2012 at 23:07
- 4$\begingroup$ @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 $\endgroup$Szabolcs– Szabolcs2012-01-20 11:56:41 +00:00Commented 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– WReach2012-01-21 19:26:43 +00:00Commented Jan 21, 2012 at 19:26
$\begingroup$ $\endgroup$
2 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} - $\begingroup$ Late answers rarely get the attention they deserve. (This one wasn't even very late.) Long overdue +1. $\endgroup$Mr.Wizard– Mr.Wizard2013-08-18 02:49:05 +00:00Commented 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– Ali Hashmi2017-02-04 13:39:10 +00:00Commented Feb 4, 2017 at 13:39
$\begingroup$ $\endgroup$
3 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} - $\begingroup$ Very cool, I was writing up something very similar. +1 $\endgroup$acl– acl2012-01-19 15:19:48 +00:00Commented 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$Leonid Shifrin– Leonid Shifrin2012-01-19 15:21:57 +00:00Commented 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– acl2012-01-19 15:30:30 +00:00Commented Jan 19, 2012 at 15:30
$\begingroup$ $\endgroup$
You could use
myList = Hold[a,b,c,d] Function[x, x=5, {HoldAll}] /@ myList // ReleaseHold $\begingroup$
$\endgroup$
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
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.) $\endgroup$Scan[]instead ofMap[](that is,/@) for multiple assignments. $\endgroup$