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.
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
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP