Message Boards Message Boards

0
|
4375 Views
|
7 Replies
|
1 Total Likes
View groups...
Share
Share this post:

How do you pass an expression to a function?

Posted 2 years ago

Trivial Case

I have a function doAnythingWith[expr_] that uses the expression expr for whatever.

(In fact, I use it to define a function with dynamic name, arguments and body. See https://community.wolfram.com/groups/-/m/t/2218132 and at the end of the attached notebook at "My actual need". At the moment this is not relevant here).

ClearAll[doAnythingWith];
doAnythingWith[expr_] := 
  Print["doAnythingWith: ", expr // InputForm, "\t", expr];

I call this function from somewhere with a simple expression. This works, of course:

Block[{x, y, expr},
expr = x/y;
Print["Caller: ", expr // InputForm, "\t", expr];
doAnythingWith[expr]
]

Caller: x/y x/y

doAnythingWith: x/y x/y

Non - trivial Case

But as soon as I use a more complex expression, it becomes difficult. I want to pass expr as is as a conditional expression If[ !TrueQ[y==0], x/y, Indeterminate] but with y replaced:

Block[{x, y, expr},
 Do[
  If[NumericQ[do], y = do, Clear[y]];
  expr = If[! TrueQ[y == 0], x/y, Indeterminate];
  Print["Caller with y = ", y, ": ", expr // InputForm, "\t", expr];
  doAnythingWith[expr];
  , {do, {y, 2, 0}}]
 ]

Caller with y = y: x/y x/y

doAnythingWith: x/y x/y

Caller with y = 2: x/2 x/2

doAnythingWith: x/2 x/2

Caller with y = 0: Indeterminate Indeterminate

doAnythingWith: Indeterminate Indeterminate*

Of course, expr is now already evaluated in the caller and not passed to the function as desired.

I tried to avoid the evaluation in the caller and then within doAnyThingWith by using Hold, Unevaluated, Inactivate and various function definitions that reverse this and/or use the HoldAll attribute. But I did not succeed.

Some of the problems are avoided by passing the expression as a string. It now arrives correctly in the function, but is evaluated there by ToExpression and is therefore again not available:

ClearAll[doAnythingWithStr];
doAnythingWithStr[exprStr_String] :=
With[{expr = ToExpression[exprStr, InputForm]},
Echo[{exprStr // InputForm}, "doAnythingWithStr.exprStr: "];
Print["doAnythingWith: ", expr // InputForm, "\t", expr]
];

Block[{x, y, exprStr},
 Do[
  If[NumericQ[do], y = do, Clear[y]];
  exprStr = "If[!TrueQ[y==0], x/y, Indeterminate]";
  Print["Caller with y = ", y, ": ", exprStr // InputForm, "\t", 
   ToExpression[exprStr, InputForm]];
  doAnythingWithStr[exprStr];
  , {do, {y, 2, 0}}]
 ]

Caller with y = y: "If[!TrueQ[y==0], x/y, Indeterminate]" x/y

doAnythingWithStr.exprStr: {"If[!TrueQ[y==0], x/y, Indeterminate]"}

doAnythingWith: x/y x/y

Caller with y = 2: "If[!TrueQ[y==0], x/y, Indeterminate]" x/2

doAnythingWithStr.exprStr: {"If[!TrueQ[y==0], x/y, Indeterminate]"}

doAnythingWith: x/2 x/2

Caller with y = 0: "If[!TrueQ[y==0], x/y, Indeterminate]" Indeterminate

doAnythingWithStr.exprStr: {"If[!TrueQ[y==0], x/y, Indeterminate]"}

doAnythingWith: Indeterminate Indeterminate

And even if it remained correct we would have the problem that a possibly globally defined value for x would be replaced immediately. So you would have to pass, save, clear and restore the variables {x, y} of the expression as well.

Does anyone have an idea how to solve this behavior?

The attached notebook shows my actual need.

Attachments:
POSTED BY: Werner Geiger
7 Replies
Posted 2 years ago

Eric, you are right. DefineFunction should hold its arguments (or at least the last one)

Consider Werners expression If[!TrueQ[x==0],1/x,Indeterminate]

If the expression is given as an explicit argument, all works fine:

In[1]:= SetAttributes[DefineFunction, HoldAll]

In[2]:= DefineFunction[name_, vars_, body_] := name = Function[vars, body]

In[3]:= DefineFunction[f, x, If[! TrueQ[x == 0], 1/x, Indeterminate]];

In[4]:= f /@ {z, 2, 0} 
Out[4]= {1/z, 1/2, Indeterminate}

But if the expression is passed as a variable reference it does not work:

In[5]:= expr := If[! TrueQ[x == 0], 1/x, Indeterminate]

In[6]:= DefineFunction[g, x, expr];

In[7]:= g /@ {z, 2, 0}
Out[7]= {1/x, 1/x, 1/x}
POSTED BY: Hans Milton
Posted 2 years ago

In the example that Hans provided, you'd need to set the attributes of DefineFunction to HoldAll (or whatever Hold* is appropriate) in order to avoid evaluation of the body before it gets used in the Function expression.

POSTED BY: Eric Rimbey
Posted 2 years ago

Examples with Function:

In[1]:= DefineFunction[name_, vars_, body_] := name = Function[vars, body]

In[2]:= DefineFunction[g, {a, b, c}, a + b c];
In[3]:= g[2, 3, 4]
Out[3]= 14

In[4]:= DefineFunction[h, {p, q}, p^(-q)];
In[5]:= h[2, 3]
Out[5]= 1/8
POSTED BY: Hans Milton
Posted 2 years ago
POSTED BY: Eric Rimbey

Possibly overkill here, but I think this might be along the lines of what you want.

ClearAll[doAnythingWith];
SetAttributes[doAnythingWith, HoldAllComplete];
doAnythingWith[origexpr_] := Module[{expr = ReleaseHold[origexpr]},
   Print["doAnythingWith: ", expr // InputForm, "\t", expr]];

Then wrap the argument in HoldComplete like so.

Block[{x, y, expr},
 Do[
  If[NumericQ[do], y = do, Clear[y]];
  expr = HoldComplete[If[! TrueQ[y == 0], x/y, Indeterminate]];
  Print["Caller with y = ", y, ": ", expr // InputForm, "\t", expr];
  doAnythingWith[expr];
  , {do, {y, 2, 0}}]
 ]

(* Caller with y = y: HoldComplete[If[ !TrueQ[y == 0], x/y, Indeterminate]] HoldComplete[If[!TrueQ[y==0],x/y,Indeterminate]]

doAnythingWith: x/y x/y

Caller with y = 2: HoldComplete[If[ !TrueQ[y == 0], x/y, Indeterminate]]    HoldComplete[If[!TrueQ[y==0],x/y,Indeterminate]]

doAnythingWith: x/2 x/2

Caller with y = 0: HoldComplete[If[ !TrueQ[y == 0], x/y, Indeterminate]]    HoldComplete[If[!TrueQ[y==0],x/y,Indeterminate]]

doAnythingWith: Indeterminate   Indeterminate *)
POSTED BY: Daniel Lichtblau
Posted 2 years ago

I wouldn't know what else to explain. I thought my post and my example notebook were already way too detailed.

Maybe it will be clearer if I just use one variable x and comments instead of real code. Attached is a new notebook version "211219 Pass Expr to Function 2" which contains this at the end.

I want to write a function defineFx that defines a global function f[x_] := body, where body is an expression set in the caller and passed to defineFx. I.e.:

Print[Style["The following code does not work!", Blue, Bold]];
ClearAll[defineFx];
(* Possibly SetAttributes[defineFx, ...] *)
defineFx[args_List, body_] := With[{myargs = Pattern[#, _] & /@ args},
   ClearAll[f];
   Evaluate[f @@ myargs] := body;
   ];

(* Within The caller *)
(* Assign a symbolic expression expr that depends on a symbolic \
variable x. *)
(* As an example define 1/x *)
Clear[x];
(* The following assignment to expr does not work since it is \
evaluated here *)
expr = If[! TrueQ[x == 0], 1/x, Indeterminate];
(* expr has to be in some form that is kept until the delayed \
evaluation of f[x]  *)
defineFx[{x}, expr];
Print[Information[f]];
Print[{f[x], f[3], f[0]}];
Print[Style["This is wrong!", Red]];

Print[];
Print[Style["What I want to get is:", Blue, Bold]];
ClearAll[f];
f[x_] := If[! TrueQ[x == 0], 1/x, Indeterminate];
Print[Information[f]];
Print[{f[x], f[3], f[0]}];
Print[Style["This is correct", Darker[Green]]];

enter image description here

Attachments:
POSTED BY: Werner Geiger
Posted 2 years ago

I'm not 100% sure I understand what you want. I've looked at the attached notebook, but it's still not clear to me what function you actually want to define. It's also not clear you me where things are going wrong for you.

It could be as simple as using a point-free style. On the other hand, we may need to use Hold or some Hold* attributes, but in that case it depends on how you want to be able to provide the dynamic information and how we can control the evaluation of expressions.

I think it would be helpful if you provided some test cases. Show us exactly how you want to be able to define a function, and then show us how you would like to use that capability to define some specific function with some sample data, and then show us how you'd use the resulting dynamically defined sample function by specifying the inputs and expected outputs. Provide the ouptuts as you would like to see them so that we can determine exactly where things are not working as you expect.

POSTED BY: Eric Rimbey
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract