How to set values to symbols

2019-06-22 04:29发布

问题:

I would like to set values to a list of variables, like so:

list[[1]] = 2

and if list[[1]] is a, then a will now be equal to two. How can I achieve this?

回答1:

Well, let's try it naively:

Make a list:

In[1]:= ClearAll[list, a, b, c]; 
list = {a, b, c}; 

It's as we expect it:

In[3]:= list
Out[3]= {a, b, c}

Set the first element to 2:

In[4]:= list[[1]] = 2
Out[4]= 2

In[5]:= list
Out[5]= {2, b, c}

That doesn't affect a:

In[6]:= a 
Out[6]= a

Start again:

In[7]:= ClearAll[list, a, b, c]; 
list = {a, b, c}; 

In[9]:= list
Out[9]= {a, b, c}

The problem is that Set (=) has HoldFirst as one of its attributes , i.e., it doesn't evaluate its first argument which is the lefthand side, and the assignment is to the list and not to the variable that's in that location. But you can force evaluation using Evaluate:

In[10]:= Evaluate[list[[1]]] = 2
Out[10]= 2

Now the list seems to be the same as before:

In[11]:= list
Out[11]= {2, b, c}

but that's only because a is still there and has gotten the value of 2 (in the previous version a was replaced by 2):

In[12]:= a
Out[12]= 2

If you now set a to 3 you'll see that that changes list too:

In[13]:= a = 3
Out[13]= 3

In[14]:= list
Out[14]= {3, b, c}

EDIT

Perhaps more close to the wording of your question, you could Map Set over the list:

In[16]:= ClearAll[list, a, b, c]; 
list = {a, b, c}; 

In[18]:= Set[#, RandomInteger[10]] & /@ list
Out[18]= {4, 8, 1}

In[19]:= list    
Out[19]= {4, 8, 1}

In[21]:= {a, b, c}    
Out[21]= {4, 8, 1}


回答2:

What you request is generally hard in Mathematica, since it is hard to imitate the pointer semantics. The following code will do specifically what you asked for, but is restricted to only symbols as list elements:

ClearAll[setPart];
SetAttributes[setPart, HoldFirst];
setPart[lst_Symbol, i_, value_] :=
  With[{heldPart =  First@Extract[Hold[lst] /. OwnValues[lst], {{1, i}}, Hold]},
    If[MatchQ[heldPart, Hold[_Symbol]],
      Set @@ Append[heldPart, value],
      lst[[i]] = value]];

Examples:

In[117]:= Clear[list, a, b]
list = {a, b, c, 4, 5};
a = 1;
b = 3;
list

Out[121]= {1, 3, c, 4, 5}

In[122]:= setPart[list, 1, 10];
{a, list}

Out[123]= {10, {10, 3, c, 4, 5}}

In[124]:= setPart[list, 5, 10];
list

Out[125]= {10, 3, c, 4, 10}


回答3:

You could perhaps do:

setSymbol[symbol_, value_] := Module[{},
    ToExpression[
        SymbolName[symbol] <> "=" <> ToString[value,TotalWidth->Infinity]
    ]
]
setSymbol[list[[1]], 2]

Though that may be a bit hackish. The correct way is by playing around with how values are Held from being evaluated, but I couldn't remember how; see other answers.



回答4:

I just had a similar problem with defining the value of symbols, when you first dont know wich symbol you want to redefine. Your answers helped me to find the right way doing that:

HoldPattern worked very well for me here, even if values have been allready set for variables.

In[253]:= Clear[a,b,c,d,list]
list = HoldPattern/@{a,b,c,d};
a=2;
Evaluate[list[[1]]]=1;
list//ReleaseHold
a

Out[257]= {1,b,c,d}
Out[258]= 1

So my solution is pretty much like the first solution by Sjoerd C. de Vries, but it protects the symbols from being evaluated inside of the list by HoldPattern.

Note, that one has to use ReleaseHold to use the list for further calculations. The variables (a,b,c,d) dont get affected by this construction.

This was my first post here, hope you like it ;-)