25
$\begingroup$

I have a list of global variables (some of them are indexed variables), for example:

varsH = Hold[U0[1], U0[2], B0, V0[1], V0[2]]

Or (if it is easier to handle) I can have them as a List of Strings:

varsS = {"U0[1]", "U0[2]", "B0", "V0[1]", "V0[2]"}

Now I need to be able to do the following things knowing only the position (i) of the variable in the list:

  1. Assign new value to the variable (the variable can already have a value)
  2. Clear the variable
  3. Get the current value of the variable
  4. Get the name of the variable as `String`

Additionally, I need to be able to

  5. Clear all the variables at once

What is the most elegant approach to this task? I know, it would be easier to have all the variables just as one indexed variable but I need names of the variables to be explicit.

By the term "elegant" I mean that every of the listed things should be written as short as possible (for a solution without helper functions). If there is no elegant way to avoid use of helper functions, they should be as clear as possible.

P.S. It is known that the variables should always be numerical or undefined.

$\endgroup$
2
  • 1
    $\begingroup$ Something like Scan[Clear, Hold[U0, B0, V0]] then? $\endgroup$ Commented Sep 7, 2012 at 8:24
  • $\begingroup$ Related: (280) $\endgroup$
    – Mr.Wizard
    Commented Aug 19, 2014 at 9:03

4 Answers 4

17
$\begingroup$

I think that "elegant" should be syntax as close to the normal handling of symbols as possible.

I shall define a function, unimaginatively named bump, that has a syntax similar to Part but which allows operations on symbols by way of Unevaluated and UpSet. If you will consider other storage formats besides Hold[v1, v2, ...] e.g. Hold @ {v1, v2, ...} this might be simplified somewhat.

func_[a___, bump[lst_, idx___], b___] ^:= 
  func[a, #, b] & @ Part[List @@@ Unevaluated @@ {lst}, {1}, idx]

Examples:

varsH = Hold[U0[1], U0[2], B0, V0[1], V0[2]];

bump[varsH] = Range[5];    (* set all values *)

bump[varsH, 3] = 8;        (* reassignment to B0 *)

varsH[[3]]                 (* recall B0 *)

ToString @ bump[varsH, 3]  (* get name of B0 as String *)

bump[varsH, 3] =.          (* Unset B0 *)

bump[varsH] =.             (* Unset all *)

This works with other operations and shapes of lists too. (For consistency I think Hold @ {...} is better but I will use the original form.)

vars2 = Hold[a, b, {c1, c2}, d];

bump[vars2, 3, 1] = 5;
bump[vars2, 3, 2] = 7;
bump[vars2, 3] += 1;

List @@ vars2  (* show the result *)
{a, b, {6, 8}, d}
bump[vars2, 3] =.;
List @@ vars2
{a, b, {c1, c2}, d}

For a simpler function upon which this one is based see:

Assigning values to a list of variable names

$\endgroup$
9
  • 1
    $\begingroup$ This is what I call "elegant"! I really enjoy your solution. The only doubt I have: are there possible side-effects of defining the UpValues in such a general way? $\endgroup$ Commented Jan 10, 2013 at 13:15
  • 1
    $\begingroup$ @Alexey Thanks. Yes, it would have side-effects. If you assume that bump is only used manually and not generated programmatically then it's probably okay. It would be safer no doubt to create a white-list of operations, or use an "environment" the way Leonid often does. $\endgroup$
    – Mr.Wizard
    Commented Jan 10, 2013 at 13:18
  • $\begingroup$ @Simon Thank you, as I've said before that means a lot coming from you. Sorry I cost you an Accept. By the way, you can shorten the code in your answer, though perhaps less readably. For example: vars[[{1}]] /. _[x_] :> (x = 20). (I like _[x_] over _@x_ here only because it looks a little like an injection syringe which I think is neat for the "injector pattern.") $\endgroup$
    – Mr.Wizard
    Commented Jan 11, 2013 at 0:58
  • $\begingroup$ On the other hand, _@x_ :> accurately represents a newbie's eye when he tries to parse the solution =P $\endgroup$
    – rm -rf
    Commented Jan 11, 2013 at 1:01
  • $\begingroup$ @rm-rf lol -- do you think I should add some explanatory text? $\endgroup$
    – Mr.Wizard
    Commented Jan 11, 2013 at 1:01
11
$\begingroup$

Here's an approach similar to Simon Woods, but using Extract itself rather then following replacement rules:

 setTo[val_] := Function[var, var = val, HoldAll]
 mySymbolName = Function[var, ToString[Unevaluated[var]], HoldAll];

Which is then used as:

 a[1] = b = c = 1;
 vars = Hold[a, b, c];

 Extract[vars, 3, setTo[42]]
 Extract[vars, 3, mySymbolName]

And you can then clear the value using:

 Extract[vars, 3, Unset]
$\endgroup$
2
  • $\begingroup$ Sorry, disregard that comment, my brain was loose. +1 (I overlooked the fact that you of course Unset the symbol and wondered why it didn't appear to have the value 42.) $\endgroup$
    – Mr.Wizard
    Commented Jan 10, 2013 at 13:14
  • 1
    $\begingroup$ @Mr.Wizard I quickly pasted the code into a notebook to test figure out if something was wrong and also wondered why c wasn't getting set, I updated the answer slightly. :) $\endgroup$
    – jVincent
    Commented Jan 10, 2013 at 13:17
6
$\begingroup$
a[1] = a[2] = b = c = 1;
vars = Hold[a[1], a[2], b, c];

Assign new value to the variable (the variable can already have a value)

Extract[vars, 1, Hold] /. Hold[x_] :> (x = 20)

Unset the variable

Extract[vars, 1, Hold] /. Hold[x_?ValueQ] :> (x =.)

Get the current value of the variable

Extract[vars, 1]

Get the name of the variable as String

Extract[vars, 1, Hold] /. Hold[x_] :> ToString[Unevaluated[x]]

Unset all variables

Cases[vars, x_?ValueQ :> (x =.), 1]
$\endgroup$
8
  • $\begingroup$ Some of the variables are indexed variables (U0[1] etc.) so Clear and SymbolName will not work. $\endgroup$ Commented Sep 7, 2012 at 8:32
  • $\begingroup$ @AlexeyPopkov, oops I missed that! I'll have another look... $\endgroup$ Commented Sep 7, 2012 at 8:43
  • $\begingroup$ Hold[x_[__]] | Hold[x_] :> Clear[x] in the new version clears all indexed variables with the same Head, not the one. $\endgroup$ Commented Sep 7, 2012 at 9:15
  • $\begingroup$ The only workaround I see at the moment is: List @@ Replace[vars, x_?NumericQ :> Unset[x], {1}]. But it is not very elegant when only one variable should be cleared. $\endgroup$ Commented Sep 7, 2012 at 9:24
  • $\begingroup$ For clearing one variable: Extract[vars, 1, Hold] /. Hold[x_?NumericQ] :> Unset[x] $\endgroup$ Commented Sep 7, 2012 at 9:30
0
$\begingroup$

if you have Mathematica 10 use Association function:

asc=<|"U0[1]"-> U0[1], "U0[2]"-> U0[2], "B0"-> B0, "V0[1]"-> V01, "V0[2]"-> V0[2]|>

this new build in container function supports extracting its content by keys, which are strings in this case, and by indexes

asc = <|"U0[1]" -> U0[1], "U0[2]" -> U0[2], "B0" -> B0, "V0[1]" -> V01, "V0[2]" -> V0[2]|>;

(*1. Assign new value to the variable (the variable can already have a value)*)

asc["B0"] = "newB0"

(* <|"U0[1]" -> U0[1], "U0[2]" -> U0[2], "B0" -> "newB0", "V0[1]" -> V01,  "V0[2]" -> V0[2]|>*)

(*2. Clear the variable*)

Delete[asc, "B0"]

(*<|"U0[1]" -> U0[1], "U0[2]" -> U0[2], "V0[1]" -> V01, "V0[2]" -> V0[2]|>*)

(*3. Get the current value of the variable*)

asc["U0[1]"]

(*U0[1]*)

(*4. Get the name of the variable as `String`*)

all the syntax behind works if the key values in association ore strings, if you need the key to be a symbol the syntax in this case will be like this:

asc[symbol] = 10; asc

(*<|"U0[1]" -> U0[1], "U0[2]" -> U0[2], "B0" -> "newB0", "V0[1]" -> V01, "V0[2]" -> V0[2], symbol -> 10|>*)

asc[symbol]

(*10*)

asc[Key[symbol]]

(*10*)

(*5. Clear all the variables at once*)

asc =.
$\endgroup$
2
  • $\begingroup$ The question asks for ways to do five tasks. Can you show how to do them with associations? $\endgroup$
    – Michael E2
    Commented Jan 14, 2015 at 16:46
  • 1
    $\begingroup$ User, I appreciate your effort on this. However following your extended examples I believe that you misunderstood the question. The question is not simply how to track a series of values by key name but rather how to use a list of strings (or keys) to change the value of other Symbols. For example one should be able to do something like asc["B0"] = "newB0" and change the value of (global) Symbol B0 to "newB0". $\endgroup$
    – Mr.Wizard
    Commented Jan 14, 2015 at 18:55

Your Answer

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

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