1

How can one convert a Mathematica expression into a string without the expression being evaluated first? I tried using Hold, but it returns Hold[expr], instead of expr. Is there any way to get just the expression?

For example, I have a variable var = 5, and I need to assign the name of var to another variable, so that var2 = "var". How can I do this?

My actual use case is this:

I am trying to synchronize my mathematica variables with certain fields in an external database. Every time the external database is updated, I call on a function that is supposed to update my corresponding Mathematica variables. It takes in a list of mappings like {var1 -> "field1", var2 -> "field2"}, and its role is to access the external database, look up the value in "field1", and assign it to var1, then look up the value in "field2" and assign it to var2. The number of variables in the mapping (and their names) can be arbitrary.

The problem is that I need those var1 and var2 variables to be passed as variable names, not values, so that I can assign them inside the function. I do the assignment using

Do[ i[[1]]->i[[2]]/.remoteData, {i,mappings} ]

where remoteData = {"field1"->"value of field 1", "field2" -> "value of field 2"}. I need i[[1]] to stand for var1, not for its value. I tried setting the attribute SetAttributes[fn,HoldAll] but that didn't help.

The result that I am looking for is var1 = "value of field 1", var 2 = "value of field 2".

verse
  • 1,287
  • 6
  • 18
  • This is a duplicate I believe so I shall not post an answer. I think the simplest is MakeBoxes[var], but how you get var into that expression will be the issue. What is your actual use for this? – Mr.Wizard Feb 09 '14 at 16:42
  • What I mean is, you could of course just type in "var" so presumably you are working with some list of Symbol names or the like that you need to manipulate. Please describe the situation and your goals. – Mr.Wizard Feb 09 '14 at 16:45
  • 1
    Other alternatives include SymbolName @ Unevaluated[var] and ToString @ HoldForm[var] and each of these may be useful, again depending on your actual needs. I await your clarification. – Mr.Wizard Feb 09 '14 at 16:49
  • The key is Unevaluated. As MrW said, you can use something similar to ToString@Unevaluated[1+1]. It gives "1 + 1". Juggling unevaluated expressions is likely to get complicated though ... – Szabolcs Feb 09 '14 at 16:56
  • In my case Unevaluated makes it impossible to look up the value of i[[1]], which is var1. So I do need the first level of evaluation that replaces i[[1]] with var1, I just don't want Mathematica to further replace var1 with its value. – verse Feb 09 '14 at 16:58
  • @user6236 Let me make sure I understand what you are doing. You have a table with headings in a text file. You want to read this table in and assign the columns to certain variables. You want to do this based on some mapping of the form "heading-name" -> variable that your code takes as input. Is this correct? – Szabolcs Feb 09 '14 at 17:09
  • You said "It takes in a list of mappings like ..." -- where did mappings come from and is it wrapped in Hold now to prevent evaluation? – Mr.Wizard Feb 09 '14 at 17:10
  • @Szabolcs, yes, that's correct. @MrW, I define a function fn[mappings_]:= Do[ i[[1]]->i[[2]]/.remoteData, {i,mappings} ], and set SetAttributes[fn,HoldAll]. – verse Feb 09 '14 at 17:12
  • Also, when I call on fn I myself decide which variable I want to map to which fields in the db. So mappings are defined by the end-user. – verse Feb 09 '14 at 17:14
  • @user6236 Okay, what is the source of the expression you are passing to fn (as parameter mappings)? – Mr.Wizard Feb 09 '14 at 17:14
  • Okay, I think I was focusing on the wrong item. What is the source of remoteData? We (or at least I) need to see the bigger picture here. – Mr.Wizard Feb 09 '14 at 17:15
  • 1
    This is not an answer, but it's related and might be interesting. @Mr.Wizard I believe I could answer this is it's reopened ... – Szabolcs Feb 09 '14 at 17:16
  • @Szabolcs Waiting for you. – Mr.Wizard Feb 09 '14 at 17:17
  • I have a Java layer that I described in my previous question here (http://mathematica.stackexchange.com/questions/41884/detecting-changes-of-variables-values), which updates remoteData whenever the server informs it of any changes. The data on the server is stored in MongoDB in JSON format, and then imported as a standard Mathematica list using ImportString[..., "JSON"] – verse Feb 09 '14 at 17:18

2 Answers2

5

It looks like I should have used HoldForm instead of Hold.

Here is my current solution that does the job:

SetAttributes[fn,HoldAll];

fn[mappings_]:= ReleaseHold[Apply[Set, HoldForm[mappings]/.remoteData, {2}]]
numbermaniac
  • 607
  • 7
  • 14
verse
  • 1,287
  • 6
  • 18
2

If I understand the question correctly, this is what you are asking:

You have a table with headings in a text file. You want to read this table in and assign the columns to certain variables. You want to do this based on some mapping of the form "heading-name" -> variable that your code takes as input.

First of all, personally I feel uneasy about programmatically manipulating variable names, but I know that many people disagree with this ... If I were doing this myself, I'd choose to retrieve the columns based on some name stored as a string (e.g. using replacement rules), but not actually assign to variables corresponding to this name. There are fewer things that can go wrong, e.g. variables won't accidentally get overwritten.


That said, here's how to do what you asked for. Here's a example table stored in a string. You'll have a file instead.

table = 
 "one  two  three
   1    2    3
   4    5    6
   7    8    9"

These are the mappings. Variable names are stored as strings to prevent evaluation:

mapping = {"var1" -> "one", "var2" -> "two", "var3" -> "three"}

We'll use this function to assign to variables:

set[s_String, v_] := ToExpression[s, InputForm, Function[x, x = v, {HoldFirst}]]

The code:

data = ImportString[table, "Table"]
values = mapping[[All, 2]] /. (First[#] -> Rest[#] &) /@ Transpose[data]
MapThread[set, {mapping[[All, 1]], values}];

Now the variables are set to the values read from the table, based on the mappings we defined:

var1

(* ==> {1,4,7} *)

The key was using the third argument to ToExpression, which allows manipulating an expression converted from a string before it gets evaluated.


However, as I said at the beginning, I would stop at creating the mapping (First[#] -> Rest[#] &) /@ Transpose[data] and using it to reference columns by their names.


EDIT: Per the OP's request, here's how to do it without storing the variable names as strings. In this case we need to store the mapping inside Hold to prevent evaluating the variables.

mapping = Hold[var1 -> "one", var2 -> "two", var3 -> "three"]

ReleaseHold[Set @@@ (mapping /. (First[#] -> Rest[#] &) /@ Transpose[data])];

Slightly simpler with a different syntax for the mapping:

mapping = Hold[var1 = "one", var2 = "two", var3 = "three"];
ReleaseHold[ mapping /. (First[#] -> Rest[#] &) /@ Transpose[data] ];
Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
  • thanks for the suggestion. I did consider passing variable names as strings to my function, but that didn't look like native Mathematica code. There must be a way to actually pass the variables themselves, but not evaluate them right away, since many built-in functions are able to do this. – verse Feb 09 '14 at 17:37
  • @user6236 The whole idea of assigning to variables progammatically based on their name makes me feel uneasy, so at this point I stopped being bothered by what looks "native" or "beautiful" ... but yes, it's possible, if you keep the mapping inside a Hold. Otherwise not. – Szabolcs Feb 09 '14 at 17:40
  • could you please clarify how one can use Hold to achieve the desired outcome? The problem I encountered was that if I put Hold around i[[1]] inside my function, it is not replaced with var1. – verse Feb 09 '14 at 17:44
  • @user6236 OK, I updated the post. Can you please choose a display name other than the auto-generated user98765-style name? I don't understand your last comment completely, so let me know if this is not what you're looking for. – Szabolcs Feb 09 '14 at 17:46