Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Given a function with the attribute HoldFirst, HoldAll or similar, and a variable, list, how can I pass the variable's name to the function without the variable being evaluated prematurely? So it is "list" that is to be passed to the function (e.g. AppendTo) instead of list.

The following method fails, as AppendTo holds the evaluation of Symbol @ "list", and since Symbol[...] is not a valid variable for which a value can be assigned, an error message appears:

list = {1, 2, 3};
AppendTo[Symbol @ "list", 1];
list
Set::write: Tag Symbol in Symbol[list] is Protected.>>

{1, 2, 3}

I should mention, that for certain reasons the obvious direct assignment below is not possible, as I must pass the list as a string:

list = {1, 2, 3};    
list = Append[list, 2]; (* this works, but is not what I am looking for *)

{1, 2, 3, 2}

The next approach also fails, as it issues an error message while returning the correct result:

list = {1, 2, 3};
held = AppendTo[Evaluate @ ToExpression["list", InputForm, Hold], 3];
ReleaseHold @ held;
list
AppendTo::rvalue: Hold[list] is not a variable with a value, so its value cannot be changed.>>

{1, 2, 3, 3}

The final approach reveals that ReleaseHold removes the outermost Hold, but leaves the innermost intact.

list = {1, 2, 3};
held = Hold @ AppendTo[Evaluate@ToExpression["list", InputForm, Hold], 4];
ReleaseHold @ held
AppendTo::rvalue: Hold[list] is not a variable with a value, so its value cannot be changed.>>

AppendTo[Hold[list], 4]
share|improve this question

5 Answers 5

up vote 15 down vote accepted

How about this:

list = {1, 2, 3};
ToExpression["list", InputForm, Hold] /. Hold[v_] :> AppendTo[v, 3]

{1, 2, 3, 3}

list

{1, 2, 3, 3}

share|improve this answer
    
This works as well for a list of variables, and seems to be a bit more straightforward than celtschk's answer. I am doing some more testing. –  István Zachar Mar 6 '12 at 22:58

This works:

ReleaseHold@Block[{list},Hold[AppendTo["list",1]]/."list"->Symbol["list"]]

The mechanism is as follows:

  • The block temporarily undefines list so that after Symbol["list"] is evaluated, evaluation stops.
  • However, the AppendTo shall not be evaluated as long as list is undefined, therefore it is wrapped in Hold.
  • Symbol shall be evaluated, therefore it may not be inside Hold. For this reason, it is injected into the Hold expression with a replacement rule.
  • When leaving the block, list gets back its old definition, therefore all we have to do now is to remove the Hold so that the generated AppendTo expression is evaluated.
share|improve this answer
    
Nice, and it can also be mapped over a list of variables! –  István Zachar Mar 6 '12 at 22:36

Injector pattern:

list = {1, 2, 3};

MakeExpression["list"] /. _[sym_] :> AppendTo[sym, 4]

Function (here using the Null syntax trick):

Function[, AppendTo[#, 4], HoldAll] @@ MakeExpression["list"]
share|improve this answer
1  
I had a different name for this pattern, and am also a big fan and frequent user of it, but Injector seems better. +1. –  Leonid Shifrin Mar 7 '12 at 6:20
    
The syntax highlighter definitely doesn't like your code, Mr.W (I do). –  István Zachar Mar 7 '12 at 10:59
    
@Istvan it took me a little bit to figure out what you mean. Normally I have "Missing arguments" highlighting turned off. Since this is AFAIK a perfectly valid use of MakeExpression I suggest that you modify your SyntaxInformation accordingly if you like to keep that setting turned on. You can also use ToHeldExpression instead if you prefer. –  Mr.Wizard Mar 7 '12 at 13:55
    
It is not just the missing second argument for MakeExpression, but 1) since there is no & for the #, it is coloured purple, and 2) the comma in Function is coloured as well. Not that I mind any of these. –  István Zachar Mar 7 '12 at 14:07

Not to detract from the existing answers (particularly @WReach's suggestion, which was the same solution that came to my mind as I read your question, and which I will use here), but you may find it easier to define your own references rather than using strings. (In fact, I wouldn't necessarily recommend an approach based on building Mathematica expressions out of strings as a good first choice in general.) The advantage of using your own operator is that it will be easier to get rid of it when you don't want it any more, and no complex syntax is needed to actually use it.

First, let us define the reference object (which is essentially just a wrapper):

Attributes[referenceTo] = {HoldAll, Listable};

referenceTo[str_String] :=
  referenceTo[Evaluate@ToExpression[str, InputForm, Hold]];

referenceTo@Hold[sym_Symbol] := referenceTo[sym];

referenceTo /: f_[argsPre___, ref : referenceTo[str_String], argsPost___] := 
 f[argsPre, Evaluate[ref], argsPost]

We can either use this directly, or on strings. For example:

{a, b, c} = Range[3]; (* make some definitions to prove it works *)

referenceTo[{"a", b, Hold[c]}]
    (* -> {referenceTo[a], referenceTo[b], referenceTo[c]} *)

So, while very simple, the behaviour of the wrapper is probably sufficient for most purposes in terms of creating the references in the first place (and, of course, the range of situations in which it is recognized can readily be expanded if necessary). Now we tell it when and how to dereference the symbols it wraps:

$dereferencingFunctions = {AppendTo, ReleaseHold};
referenceTo /: f_[argsPre___, referenceTo[sym_Symbol], argsPost___] /; 
 MemberQ[$dereferencingFunctions, f] :=
  f[argsPre, sym, argsPost]

You can now manipulate $dereferencingFunctions to switch this behaviour on and off for different functions as required and avoid the need for manual dereferencing as part of each call. Thus,

list = {elem1};
AppendTo[referenceTo[list], elem2];
stuff = elem3;
AppendTo[referenceTo["list"], referenceTo[stuff]];
(* a rather odd way to use it, but possibly useful: *)
AppendTo @@ referenceTo[{"list", elem4}];

evaluates correctly and without producing any messages, and gives:

list
    (* -> {elem1, elem2, elem3, elem4} *)

Or, if you prefer:

ReleaseHold@referenceTo["list"]
    (* -> {elem1, elem2, elem3, elem4} *)

In my opinion, despite its extreme simplicity, this approach manages to be much more practical than using strings directly.

share|improve this answer
    
While I agree with you that a proper wrapper for referencing would be the best solution, at the moment I still try to solve my issues with structural replacements. Your solution seems robust, but I wouldn't say it is simple. At least not at 2am :) –  István Zachar Mar 7 '12 at 1:49
    
Would it be possible to extend this approach, so that this would work as expected x=5; referenceTo["x"] = 15; x? In other words, so that variables could be set by reference as well? –  Ajasja May 22 '12 at 12:54
    
@Ajasja: Yes, just write AppendTo[$dereferencingFunctions, Set] and it works automatically. (This won't work for list assignments, like {q, referenceTo[r], referenceTo["s"]} = {1, 2, 3}, though. Some changes would be needed to support that.) –  Oleksandr R. May 22 '12 at 22:04
    
Thanks! I actually tried that, but it was giving me a warning that went away upon restarting a fresh kernel. Is it safe to use this with SetDelayed as well? –  Ajasja May 23 '12 at 8:00
    
@Ajasja: yes, there shouldn't be any evaluation leaks as the arguments are passed right through with the symbols dereferenced. The approach is so simple that I'm sure you'll be able to find some corner cases, but for typical usage I think it is robust enough (at least, better than using strings, which was the main point here). –  Oleksandr R. May 24 '12 at 0:29

Here is yet another possibility (which, in a way, combines some of the suggestions given already): define a new scoping construct, most similar to Function, to perform the task you need:

ClearAll[strFunction];
SetAttributes[strFunction, HoldAll];
SyntaxInformation[strFunction] =
     {"ArgumentsPattern" -> {_, _}, "LocalVariables" -> {"Solve", {1, 1}}};
strFunction[var_, expr_] :=
   Function[strvar,
      ToExpression[strvar, InputForm,
          Function[held, 
             Unevaluated[expr] /.HoldPattern[var] :> held, 
             HoldAll
          ]
      ]
   ];

You can use it as:

list = {1, 2, 3};
strFunction[l, AppendTo[l, 1]]["list"]

Mapping on a list of such names is also quite easy in this approach:

Map[strFunction[l, AppendTo[l, 1]], list-of-symbol-names]

You can also add type-checks, generalize to functions with more than one variable, etc.

share|improve this answer
    
@Ajasja Ok, I see - there is indeed an evaluation leek. Sorry, you spotted it correctly. Should change Unevaluated[var] :> held to HoldPattern[var] :> held - will edit in a moment. Thanks for spotting this - I am getting older :). Will delete my erroneous comments. –  Leonid Shifrin May 22 '12 at 11:59

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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