TransWikia.com

Construct Function[ ] from String based formal arguments

Mathematica Asked by Robert Nowak on June 30, 2021

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.

4 Answers

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]

Answered by Szabolcs on June 30, 2021

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.

Answered by Robert Nowak on June 30, 2021

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]

Answered by Robert Nowak on June 30, 2021

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}  *)

Answered by Michael E2 on June 30, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP