16
$\begingroup$

As part of a calculation I need to do something like this

Evaluate[{aaa, bbb, ccc}[[ index]]] = {1, 2, 3, 4, 5} 

so if index is 1 then {1, 2, 3, 4, 5} will be stored into the variable aaa.
But if I re-evaluate this it does not work because aaa is now a list and not a variable. I tried various options with Hold[] etc but did not manage to solve this.

$\endgroup$
3
  • $\begingroup$ Related: (1), (2), (3), (4), (5) $\endgroup$ Commented Jun 6, 2012 at 23:49
  • $\begingroup$ All: please comment if you believe this question or any of the "Related" ones should be merged. $\endgroup$ Commented Jun 6, 2012 at 23:54
  • 1
    $\begingroup$ By the way please also see: Elegant manipulation of the variables list $\endgroup$ Commented Aug 27, 2014 at 6:52

3 Answers 3

25
$\begingroup$

This is a fairly natural question and I feel it is worthy of attention. I am going to answer in two parts. First, I am going to show a method that is more appropriate for Mathematica programming and which I recommend you use instead. Then I will show how to force the action you are attempting.


Better Alternatives

The common way to accomplish programmatically selected assignments is to use indexed variables. This allows you to assemble a "variable" from inert parts. For example, one would use a single variable var and simply make assignments (SeedRandom[1] for a consistent result):

SeedRandom[1] Do[ var[i] = RandomInteger[9], {i, {1, 2, 3, 2, 3, 1, 3}} ] 

Or recall them:

var /@ {1, 2, 3} 
{0, 7, 8} 

If you desire a certain name be attached to a value you can index with Strings.

names = {"aaa", "bbb", "ccc"}; i = 1; var[ names[[i]] ] = Sqrt[2]; (* dummy first assignment *) var[ names[[i]] ] = {1, 2, 3, 4, 5}; var["aaa"] 
{1, 2, 3, 4, 5} 

In passing, depending on your application you may find Rules applicable.

Associations

Mathematica 10 introduced Associations which are like self-contained "indexed variables." Use is similar but you need to start with an (optionally empty) Association before you make assignments. Example:

SeedRandom[1] asc = <||>; Do[asc[i] = RandomInteger[9], {i, {1, 2, 3, 2, 3, 1, 3}}] asc 
<|1 -> 0, 2 -> 7, 3 -> 8|> 

Values may be recalled using Map, Replace, or Lookup; for a comparison see:

For some ideas of when and why one might use associations over "indexed variables" see:


Forcing the behavior

Suppose you need the behavior you asked for to keep a large program working without extensive modification.

Method #1

This works because Part preserves the head of the expression, here Unevaluated.

Ignore the syntax highlighting in Unevaluated: this is a nonstandard but safe use.

This could easily use the same syntax as Method #2: assign[symbols_, idx_, val_] :=

ClearAll[aaa, bbb, ccc, assign] assign[idx_, val_] := (# = val) & @ symbols[[1, {idx}]] symbols = Hold @ Unevaluated[aaa, bbb, ccc]; assign[1, "dummy"]; assign[1, Range@5]; aaa 
{1, 2, 3, 4, 5} 

Method #2

This uses the injector pattern in preference to Unevaluated.

ClearAll[aaa, bbb, ccc, f1, assign] assign[symbols_, idx_, val_] := symbols[[{idx}]] /. _[x_] :> (x = val) symbols = Hold[aaa, bbb, ccc]; assign[symbols, 1, "dummy"]; assign[symbols, 1, Range@5]; aaa 
{1, 2, 3, 4, 5} 
$\endgroup$
3
  • $\begingroup$ Cool! For Association Lists I would add ClearAll[assign]; SetAttributes[assign, HoldFirst]; assign[association_, key_, val_] := Hold[association[key]] /. _[x_] :> (x = val) $\endgroup$ Commented Jul 19, 2017 at 19:24
  • 1
    $\begingroup$ @tchronis You should not need the injector pattern here; a straightforward assignment will work: SetAttributes[assign, HoldFirst]; assign[association_, key_, val_] := association[key] = val. Much of the time I would just write out (asc[#] = #2) & however if I needed to apply that across a set of keys and values. $\endgroup$ Commented Jul 20, 2017 at 6:39
  • $\begingroup$ you are right. I overcomplicated things here. When the keys are themselves global variables it can be confusing (that was my case). Thank you. $\endgroup$ Commented Jul 20, 2017 at 7:35
3
$\begingroup$

Keep data out of your variable names

Use functional programming where, whenever you use aaa, it is an argument to a function. If you want to pass something different in, call the function.

If you really want to do this, use one of the HoldAll-like attributes here.

$\endgroup$
2
$\begingroup$

I would not recommend it, but you could do something like

varlist = "var1,var2,var3"; index = 2; ToExpression[StringSplit[varlist, ","][[index]] ~~ "={1, 2, 3, 4, 5}"]; var2 (* -> {1, 2, 3, 4, 5} *) 
$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.