3

I'd like to make a function that takes a list of variables and returns a corresponding rule list with the current values of the variables. E.g.

x = 1;
y = 2;
VariablesToRules[{x, y, z}]
(* {x -> 1, y -> 2, z -> z} *)

Is this even possible?

march
  • 23,399
  • 2
  • 44
  • 100
Chris K
  • 20,207
  • 3
  • 39
  • 74
  • 1
    This is a super interesting question. This post is related, because you will need to get the symbol names in order to Clear them, but it's not clear how to short-circuit the evaluation when you will be feeding a list of variable names to the function rather than just the variable name. – march Feb 06 '19 at 20:34
  • 1
    Is OwnValues /@ Unevaluated@{x, y, z} OK? – xzczd Feb 07 '19 at 03:28

2 Answers2

4

Update 2

Based on a suggestion by Somos, the following version is nicer. According to what the OP wants:

SetAttributes[variableToRule, {HoldAll, Listable}]
variableToRule[var_] := SymbolName@Unevaluated@var -> var

and according to my original interpretation of the problem:

SetAttributes[variableToRule, {HoldAll, Listable}]
variableToRule[var_] := Module[{val = var}, Clear@var; var -> val]

Update 1

After some comments from the OP, it seems they want instead something like

variableToRule[var_] := SymbolName@Unevaluated@var -> var

instead.

Original Post

Here's a first iteration. First define the helper function,

ClearAll@variableToRule
SetAttributes[variableToRule, HoldAll]
variableToRule[var_] := Module[{val = var}
 , Clear@var
 ; var -> val
 ]

Then, the function is

ClearAll@variablesToRules
SetAttributes[variablesToRules, HoldAll]
variablesToRules[vars_List] := variableToRule /@ Unevaluated@vars

This uses the trick from this answer.

Then,

x = 1; y = 2; z = 3;
variablesToRules[{x, y, z}]
(* {x -> 1, y -> 2, z -> 3} *)
march
  • 23,399
  • 2
  • 44
  • 100
  • Thanks - any way to keep the variables' values afterwards? – Chris K Feb 06 '19 at 20:57
  • @ChrisK. Your question confuses me. If you're making replacement rules, isn't the whole point that you will use them to replace the values of x and y, etc. in expressions that contain those variables? In that place, you don't want x and y set before-hand. What is it that you are trying to do here? Is this just for display purposes or something? If you keep the variables set, then you will get {1 -> 1, 2 -> 2}. – march Feb 06 '19 at 21:02
  • I think the trick from your link works: variableToRule[var_] := SymbolName[Unevaluated@var] -> var seems OK – Chris K Feb 06 '19 at 21:02
  • But then, what is the point of the replacement rule? Because that won't actually work as a replacement rule, because you have the symbol name (which is a string), instead of the symbol itself. – march Feb 06 '19 at 21:04
  • It's a bit hard to explain, but I've got an inner function that takes a list of rules as an argument, which needs to be defined in an outer function where the variables are already defined. Anyhow I think I'm sorted now. Thanks! – Chris K Feb 06 '19 at 21:07
  • Obviously I don't necessarily have enough info, but can't you scope the inner function using Block or something? – march Feb 06 '19 at 21:10
  • Perhaps, but SymbolName[Unevaluated[]] is an easy enough solution for me! – Chris K Feb 06 '19 at 21:14
  • A slight improvement is the code SetAttributes[variableToRule, {HoldAll, Listable}] and variableToRule[var_Symbol] := SymbolName@Unevaluated@var -> var and variableToRule[var___] := variableToRule[List@var] – Somos Feb 06 '19 at 21:37
  • @Somos. Yes, I definitely like that better, although since the OP explicitly asked for feeding a list to the function, I think variableToRule[var_List] := variableToRule @ var makes that work. – march Feb 06 '19 at 23:07
  • Using attribute Listable is a general way to make it work also. – Somos Feb 06 '19 at 23:12
  • @Somos. Yeah, I just figured that out! I'm editing my post with your suggestions. – march Feb 06 '19 at 23:12
4

Pass in the names of the symbols as strings, and the rest is quite easy:

ClearAll[varsToRules];
varsToRules[s_] := With[{t = Map[Symbol, s]},
    s // Apply[ClearAll];
    MapThread[Rule, {Symbol /@ s, t}]
];

ClearAll[x, y];
{x, y, z} = {1, 2, 3};
varsToRules[{"x", "y", "z"}]
(* {x -> 1, y -> 2, z -> 3} *)
Shredderroy
  • 5,249
  • 17
  • 26