49
$\begingroup$

I am trying to convert a list of string names into symbols, which will then be used to store data. I have 24 files (where the name of each file is a member of the list mentioned above) that I need to process, which is why I am trying to accomplish my goal progammatically. The code below results in a Tag warning. I have read the normal documentation sources, and appreciate that you can not inappropriately assign something to a protected symbol, but this doesn't help me solve the problem.

Would you have any advice how to accomplish my goal?

Table[(ToExpression[mmsignalnames[[i]]] = 
   Extract[ToExpression[celfilenames[[i]]], mmammindices[[j]]]), {i, 
  1, Length[mmsignalnames]}, {j, 1, Length[mmammindices]}]

Set::write: Tag ToExpression in ToExpression[mmsignalGSM356796] is Protected. >>
$\endgroup$
1
  • 4
    $\begingroup$ Creating many different symbols is an error-prone practice, because you need ways to manage them, which is hard in this case. What you seem to be needing is a hash-table, with keys being your names (as strings) - this is the second suggestion in the answer of @Mr.Wizard. $\endgroup$ Commented Jan 26, 2012 at 23:19

5 Answers 5

37
$\begingroup$

One solution is to use the third argument of ToExpression:

With minimal modification, a working version of your code would look like this:

Table[
  ToExpression[
  mmsignalnames[[i]], 
  InputForm, 
  Function[name, 
    name = Extract[ToExpression[celfilenames[[i]]], mmammindices[[j]]],
    HoldAll]],
 {i, Length[mmsignalnames]}, {j, Length[mmammindices]}]

(Untested because I don't have your data; but see below for the main idea and a small demonstration.)

The core of the method is this:

ToExpression["a", InputForm, Function[name, name = 1, HoldAll]]

ToExpression will wrap the result into its third argument before evaluating it. We can make the third argument a function that sets a value to the symbol (in this simple example it always sets the value to 1). HoldAll is needed to make sure the symbol won't evaluate when it is passed to the function.


You might find all the evaluation control I'm using here a bit confusing. To learn how to work with unevaluated expressions, I recommend reading

It is one of the best tutorials on the matter.


Finally, after answering your actual question, I'd like to suggest you use a hash table instead of symbols:

Instead of creating symbols from the strings "a", "b", "c", ..., and assigning to them, you could assign to myTable["a"], myTable["b"], ... This will make programmatic access to this data trivial. You won't need to bother with evaluation control nearly as much. And more importantly, you can avoid accidental name collisions with existing symbols. Here's an example:

(myTable[#] = 1) & /@ {"a", "b", "c"}
$\endgroup$
2
30
$\begingroup$

So recently I've learned from John Fultz that RawBoxes are kind of verbatim indicator for MakeBoxes which is not well stressed out in documentation.

This or I've missed the point but it doesn't matter, here we have handy way to do this:

x = 5;
ToExpression @ MakeBoxes[RawBoxes["x"] = 123];
x
123
$\endgroup$
2
  • 1
    $\begingroup$ +1 for an exceptionally clear illustration of RawBoxes $\endgroup$
    – Mr.Wizard
    Commented May 11, 2015 at 8:26
  • 1
    $\begingroup$ @Mr.Wizard My life would have been waaay more easier if somebody have shown me this 2-3 years ago :( But I'm glad I've found and understood this. I think this is one of things I was missing in a long time. Thanks for +1 :) $\endgroup$
    – Kuba
    Commented May 11, 2015 at 8:28
16
$\begingroup$

You need to make the left-hand side of Set a symbol at the time of evaluation. Use With or similar to inject the symbol:

mmsignalnames = {"one", "two", "three"};

With[{lhs = Symbol[mmsignalnames[[2]]]},
 lhs = 5
];

two
5

Another approach that could be important if you are trying to make assignments to symbols that already have values is this:

Function[{lhs}, lhs = 7, HoldAll] @@ MakeExpression[ mmsignalnames[[2]] ] ;

two
7

Or a bit more terse using the "injector pattern":

MakeExpression @ mmsignalnames[[2]] /. _@x_ :> (x = 9);

two
9

Also, it bears mentioning that one may also use indexed symbols such as

name["two"] = 5;

name["two"]
5

For another approach to this kind of problem see:

$\endgroup$
2
  • 2
    $\begingroup$ In the first case when you know your symbols haven't been defined yet, you could shorten it with Evaluate[ToExpression[mmsignalnames[[2]]]]=5 $\endgroup$
    – Rojo
    Commented Jan 27, 2012 at 15:51
  • $\begingroup$ @Rojo yes, you can; I think that With is more flexible however, and I thought it would be easier to use in practice, though I admit I did not actually try it both ways (I would prefer indexed symbols myself). $\endgroup$
    – Mr.Wizard
    Commented Jan 28, 2012 at 1:58
12
$\begingroup$

In Mathematica version 10, you can also use Inactive to allow the Symbol to be created before doing the assignment.

Here is an example:

Clear["x"];
Activate[Inactive[Set][Symbol["x"], 3]]

(* ==> 3 *)

x

(* ==> 3 *)
$\endgroup$
8
$\begingroup$

This answer is in the same spirit as Kuba's answer, but it avoids conversion between boxes and expressions.

Here is our workhorse

mark::usage = "Use in combination with markedExpression";
markedExpressify::usage = 
  "Use mark to mark strings for conversion to expressions";
markedExpressify[expr_] :=
 Module[{pos, hCExpr, ext, thr, rLH, expresser},
  hCExpr = HoldComplete[expr];
  pos = Position[hCExpr, Unevaluated@mark[_] ];
  expresser =
   Function[
    Null,
    ToExpression @@ {Unevaluated @@ #, InputForm, HoldComplete},
    HoldAllComplete
    ];
  ext = Join@@Extract[hCExpr, pos, expresser];
  thr = Thread[RuleDelayed @@ {HoldComplete @@ pos, ext}, 
    HoldComplete];
  rLH = List @@@ Hold@Evaluate@thr;
  First[ReplacePart @@ {hCExpr, Unevaluated @@ rLH}]
  ]

We can then do something like

markedExpressify[Unevaluated[mark[#] = f[#] = 123]] & /@ {"y", "x"};
f["x"]
x
123  
123

It works even if mark has a value, though this is something you should probably avoid.

mark = 6;
markedExpressify[Unevaluated[mark["z"] = 5]];
markedExpressify[Unevaluated[mark["g[2]"] = 3]];
z
g[2]
5
3

I did not give the function attribute HoldAll, because this way it works more nicely with held expressions.

markedExpression = Hold[mark@"x" := f@mark@"y" = 5];
markedExpressify@markedExpression
ReleaseHold@markedExpressify@markedExpression
x
f[y]
Hold[x:=f[y]=5]
5
5
$\endgroup$

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.