Message Boards Message Boards

0
|
4239 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

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

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

I was hoping you could give me some insight into your overall objective, but I think we're having some communication impedence because of unstated assumptions. So, I'll try to work up from basic principles. You could just jump to Daniel's example and take note of the Hold and ReleaseHold pattern, but maybe the following will also help.

"How do you pass an expression to a function". You just pass it. Based on your examples, though, this isn't quite what you wanted to ask. What you seem to want is to assign an unevaluated expression to a variable and then pass that variable to a function. Well, this is easy. The way to keep an expression from being evaluated is to wrap it in Hold (or HoldComplete or whichever Hold* wrapper is appropriate for your situation). That entire Hold-wrapped expression is now available for whatever inspection you want to perform on it (pattern matching, extracting parts, etc). You can assign it to a variable and then reference that variable wherever you want.

In your notebook you say: "The defineF function is to define a global function with dynamic name, arguments and delayed body". This is a completely different question. The mechanisms for defining a function are already available with normal Mathematica patterns, and these mechanisms already deal with the need to hold expressions in unevaluated form. If you think about it, this must be the case. You wouldn't be able to inspect functions at all (e.g. with Information) otherwise.

So, if you want a function named myFunc that takes formal argument variables x and y and evaluates a body that uses those formal argument variables such as If[y=0=0,Indeterminate,x/y], then you can use SetDelayed:

myFunc[x_,y_]:=If[y === 0, Indeterminate, x/y]

In effect, SetDelayed is the defineF that you're trying to define. But you know all of this. So, this is why I was asking about your ultimate objective. I understand that it's obvious in your mind, but if you can't elaborate, then I'll have to make some inferences.

Maybe you want to define an expression that represents the computation of a function and then assign that computational expression to a variable name. The simplest way to do that is to just use Function:

myFunc = Function[{x, y}, If[y === 0, Indeterminate, x/y]]

The Function expression on the right hand side could have been built up in whatever manner you want. Function has the HoldAll attribute, so you don't have to worry about premature evaluation.

Understand that this is different than SetDelayed. It has to do with OwnValues, UpValues, DownValues, and so forth. I don't know whether this is relevant in your context.

At this point you might be thinking, "but I want a DefineFunction function that takes three arguments: a function name, an argument list, and an expression body". But I'm going to assume that this thought is due to a misunderstanding. I know that's arrogant and dangerous, but I just can't see my way around it without more context from you. Here's the problem, the argument list and the expression body cannot be independent. You seem to be thinking of the body as an expression using formal variables. Thus, the argument list must be provided to indicate which are the formal variables. But formal variables are just a way to bind things. In the above example, I could have used this function:

Function[If[#2 === 0, Indeterminate, #1/#2]]

So, I'm guessing (and I apologize if I'm just missing a critical piece of your context) that what you really want is a way to bind an arbitrary variable to a function. I'm guessing that the need to separate out the computational expression from the argument list was just due to you thinking that you needed to dynamically generate a SetDelayed expression.

If I'm on the right track here, then all you need to do to make this process "dynamic", is to define a function that takes a Symbol (to use as the function name) and a Function expression that will be used to do the computation.

DefineFunction[name_Symbol, body_Function] := (name = body)

Now, if you don't like requiring the body to already be a Function, and instead you just want to pass in an expression, then there is a real semantic problem here, because you'll have to infer the formal arguments. You may be thinking, "exactly, that's why I wanted the arguments of DefineFunction to include an argument list for the formal arguments of the function to be defined". But the problem here is that if the caller has enough information to provide the formal arguments together with the expression that uses them, then the caller already has enough information to define a Function. If you provide a convenience function for defining a function with an argument list and an expression body, then you've basically re-defined Function.

I hope that with this information you can see your way to getting the behavior you want. I'm worried that I'm completely off base because I've misunderstood your actual objective, but I don't see how to proceed any further without more clarification from you.

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'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
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
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