3
$\begingroup$

If wee look at:

x=11
f=Function[{x},x^2]
f[5]

we get:

11
Function[x, x^2]
25

as expected.

Now suppose we want to create the Function[] withe a formal parameter based on a String:

Clear@x
f = Function[Evaluate@{Symbol@"x"}, x^2]
f[5]

luckily this also works as expected, we get:

Function[{x}, x^2]
25

now we try:

x = 11
f = Function[Evaluate@{Symbol@"x"}, x^2]
f[5]

and we get the misery:

Out[140]= 11

During evaluation of In[140]:= Function::flpar: Parameter specification {11} in
Function[{11},x^2] should be a symbol or a list of symbols.

Out[141]= Function[{11}, x^2]

During evaluation of In[140]:= Function::flpar: Parameter specification {11} in
Function[{11},x^2] should be a symbol or a list of symbols.

Out[142]= Function[{11}, x^2][5]

The problem originates from some tabular ascii data with named columns. The names of the columns are of course strings. This names should be used as names for the formal parameters of the Function[] to be built.

Any idea how this can be fixed? thx for any hint.

$\endgroup$
3
  • $\begingroup$ does ClearAll[f];f = Function[, (Symbol@"x")^2] give what you need? $\endgroup$
    – kglr
    Jun 7, 2021 at 10:03
  • $\begingroup$ @kglr this does not work at all. It defines a Function with no formal parameters at all an returns 11^2 = 121. $\endgroup$ Jun 7, 2021 at 10:09
  • $\begingroup$ what was i thinking:). Try ClearAll[f]; f = Module[{arg = Symbol["x"]}, Function[arg, arg^2]] or ClearAll[f]; f = Block[{arg = Symbol["x"]}, Function[arg, arg^2]]? $\endgroup$
    – kglr
    Jun 7, 2021 at 11:15

4 Answers 4

2
$\begingroup$

It seems that you are looking to obtain the symbol x, in unevaluated form, from the string "x".

The following will not give x, but the values of x.

x = 123
Symbol["x"]

The solution is the 3rd argument of ToExpression: ToExpression["x", InputForm, Hold]. Now you have the symbol x, not its value. Manipulating it will require great care if you must avoid evaluation.

You can for example do

Hold[x] /. Hold[a_] :> Function[a, a^2]
$\endgroup$
3
  • $\begingroup$ tried Hold[Symbol["x"]] /. Hold[a_] :> Function[a, a^2] does not work and even if it worked, it would prevent the objective to use the names as given by the string of the formal parameter. I do not want to refer to the parameter as a but as x, "x", #x, or similar. $\endgroup$ Jun 7, 2021 at 10:39
  • $\begingroup$ Ok, got the trick: ToExpression["x", InputForm, Hold] /. Hold[a_] :> Function[a, a^2] but still using a inside the expression/Function defined instead of x, that is what I would like to avoid. As already noted the formal parameter name comes from the headings of an ascii table, this name should be used in a mnemonic way. $\endgroup$ Jun 7, 2021 at 10:44
  • $\begingroup$ based on your idea: define/construct a function: In[241]:= Clear@x; Hold[Symbol /@ {"x", "y", "z"}] /. Hold[a_] :> Function[Evaluate@a, y + z] %[10, 20, 30] Out[241]= Function[{x, y, z}, y + z] Out[242]= 50 but still fails if x is globally assigned. In[243]:= x = 11; Hold[Symbol /@ {"x", "y", "z"}] /. Hold[a_] :> Function[Evaluate@a, y + z] %[10, 20, 30] During evaluation of In[243]:= Function::flpar: Parameter specification {11,y,z} in Function[{11,y,z},y+z] should be a symbol or a list of symbols. Out[243]= Function[{11, y, z}, y + z] During evaluation of In[243]:= Function::flpar $\endgroup$ Jun 7, 2021 at 11:00
1
$\begingroup$

Some slight variations:

x = 11; y = -3; (* to check leaks *)

(* single variable *)
vars = {"x"};
f = Thread[ToExpression[vars, StandardForm, Hold], Hold] /. 
  Hold[v_] :> Function[v, x^2]
(* multivariable *)
vars = {"x", "y"};
f = Thread[ToExpression[vars, StandardForm, Hold], Hold] /. 
  Hold[v_] :> Function[v, x^2 y]

(* Apply[Function] instead of ReplaceAll[] *)
(* body given as String *)
vars = {"x", "y"};
body = "x^2y";
f = Function @@ Join[
   Thread[ToExpression[vars, StandardForm, Hold], Hold], 
   ToExpression[body, StandardForm, Hold]]

Addendum

It seems this might be an XY problem, so I'll throw out an approach to the following remark by the OP:

For the use of this imagine a big ascii table with many, many columns each headed by a name (string). Now suppose you have retrieved a list of strings containing the headings e.g. 10, 20 or more names. Following we do not want to take care of all of them, but just a small couple of columns and define a function capable to operate on all the rows of the table with regard of the columns of interest. -- This answer

Here's a table tab along the lines described, converted to a dataset ds:

SeedRandom[1];
tab = Prepend[RandomInteger[10, {5, 9}], CharacterRange["r", "z"]];
ds = Rest@tab //
   Map[AssociationThread[tab[[1]] -> #] &] //
  Dataset

A function can operate on all the rows of the table with regard of the columns of interest through the Map operators and #name arguments. For instance ds[Map[#x^2 &]] results in a column of the squared values of the column "x", and Normal@ds[Map[#x^2 &]] gives a regular List of values. Here are some ways to structure and format the output:

mapFormula[e_ &] := 
  With[{key = StringDelete["#"]@ToString[e, StandardForm]},
   Map[<|key -> e|> &]
   ];

ds[Map[#x^2 &]]
ds[Map[<|"x^2" -> #x^2|> &]]
ds[mapFormula[#x^2 &]]

If the data is in a CSV file, then it can be imported as a dataset:

(* we'll use a String representation of a CSV file *)
csvfile = ExportString[tab, "CSV"]; 
ds = ImportString[csvfile, {"CSV", "Dataset"}, "HeaderLines" -> 1]

(* output is the same as  ds  above *)

Update: another approach using tab

The first form evaluateWithTable[body, tab, Listable] works if the expression in the string body is a listable/vectorized expression. Otherwise call it without the Listable argument and the function represented by body will be applied to each data row in tab. The code assumes the first row in tab are strings that are valid symbol names.

evaluateWithTable // ClearAll;
evaluateWithTable[body_String, tab_, Listable] := Module[{vars},
   vars = tab[[1]];
   Thread[ToExpression[vars, StandardForm, Hold], Hold] /. 
    Hold[v_] :>
     Block[v,
      v = Transpose@Rest@tab;
      ToExpression@body
      ]
   ];
evaluateWithTable[body_String, tab_] := Module[{vars},
   vars = tab[[1]];
   Apply[
    Function @@ Join[
      Thread[ToExpression[vars, StandardForm, Hold], Hold], 
      ToExpression[body, StandardForm, Hold]],
    Rest@tab,
    2]
   ];

Example:

evaluateWithTable["x^2+y", tab, Listable]
(*  {70, 4, 21, 3, 18}  *)

evaluateWithTable["x^2+y", tab]
(*  {70, 4, 21, 3, 18}  *)
$\endgroup$
1
  • $\begingroup$ thx for your suggestions, In deed I tried to mimic Dataset i some simpler fashion. I thought the Dataset approach is to clumsy. What I dislike about it, is that every singe data item is wrapped by a Rule[ ] all along every column in a redundant manner. However the comfort with Dataset is top so I will go for it. $\endgroup$ Jun 9, 2021 at 17:08
0
$\begingroup$

Here we have the final Function construction:

MakeFunktion[pList : {_String ...}, Function[e_]] :=
 ReleaseHold@Quiet@
   Function[
    Evaluate[ToExpression[pList, InputForm, Hold]],
    Evaluate[e /. Slot[s_] :> ToExpression[s, InputForm, Hold]]
   ]

This is how to use it:

In[435]:= a = 11
f = MakeFunktion[{"a", "b", "c"}, Sqrt[#c^2 + #a] &]
f[10, 20, 30]

Out[435]= 11

Out[436]= Function[{a, b, c}, Sqrt[a + c^2]]

Out[437]= Sqrt[910]
$\endgroup$
0
$\begingroup$

Ok thanks to the ToExpression hint by @Szabolcs I got the magic working:

In[392]:= x = 11
f = ReleaseHold@Quiet@
Function[Evaluate@ToExpression[{"x", "y", "z"}, InputForm, Hold], y + z]
f[10, 20, 30]

Out[392]= 11

Out[393]= Function[{x, y, z}, y + z]

Out[394]= 50

Just Function[] is a little bit complaining so we need Quiet[].

For the use of this imagine a big ascii table with many, many columns each headed by a name (string). Now suppose you have retrieved a list of strings containing the headings e.g. 10, 20 or more names. Following we do not want to take care of all of them, but just a small couple of columns and define a function capable to operate on all the rows of the table with regard of the columns of interest.

$\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.