16

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.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
BlueMac
  • 711
  • 5
  • 13

3 Answers3

25

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}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Cool! For Association Lists I would add ClearAll[assign]; SetAttributes[assign, HoldFirst]; assign[association_, key_, val_] := Hold[association[key]] /. _[x_] :> (x = val) – tchronis Jul 19 '17 at 19:24
  • 1
    @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. – Mr.Wizard Jul 20 '17 at 06:39
  • you are right. I overcomplicated things here. When the keys are themselves global variables it can be confusing (that was my case). Thank you. – tchronis Jul 20 '17 at 07:35
3

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.

ninjagecko
  • 131
  • 2
2

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} *)
Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453