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

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$
    – tchronis
    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$
    – Mr.Wizard
    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$
    – tchronis
    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$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.