Message Boards Message Boards

1
|
6793 Views
|
11 Replies
|
10 Total Likes
View groups...
Share
Share this post:

How to define a function dynamically?

Posted 3 years ago

I want do define a (local or global) function with a dynamic name, arguments and body. The name comes from a string funcName, the arguments from a list args and the body from an expression body.

I.e.: If

funcName = "funcX"; args = {x,y}; body = (x+y)^2; 

are given, I want do define a function

funcX[x_ , y_ ] := (x+y)^2;

I tried many things and read a lot of docs to achieve that, but I could not succeed. Can anybody help me?

POSTED BY: Werner Geiger
11 Replies

Here is a pretty concise solution using Inactive:

Activate[Inactive[SetDelayed][ToExpression[funcName]@@(Pattern[#,_]&/@args),body]]

and another using With:

With[{funcName=ToExpression[funcName],args=Pattern[#,_]&/@args,body=body},
    Evaluate[funcName@@args]:=body
]
Posted 3 years ago

Thanks a lot, Christopher!

Both methods work perfectly. I'm impressed.

By your and Neils work my problem is completely solved now.

Regards (Sorry for being late),

Werner

POSTED BY: Werner Geiger

I should have described how this works.

I created a local (temporary) function myfun and defined it with the arguments you pass in and set it to the body (evaluated) that you passed in. After this, I define a global function that has the same DownValue as the temporary function with the name replaced. The DownValue is the heart of what makes the rule pattern match and substitute.

The other tricky thing is taking a list of argument symbols and turning them into a sequence of patterns by mapping a function over the arguments and changing List to Sequence afterwards.

Regards

Neil

POSTED BY: Neil Singer
Posted 3 years ago

Thanks a lot, Neil!

This works perfectly.

Regards (Sorry for being late)

Werner

POSTED BY: Werner Geiger

Ok, Werner,

You sparked my interest! Here is what you asked for:

makefun[name_String, args_List, body_] := 
 Module[{myfun}, 
  myfun[(Map[Pattern[#, Blank[]] &, args]) /. List -> Sequence] := 
   Evaluate[body]; Clear[name]; 
  DownValues[name] = DownValues[myfun] /. myfun -> Symbol[name];]    

nargs = {x, y};
nbody = (x + y)^2;


makefun["funcX", nargs, nbody]

This will construct a "function" that is identical to the definition you get with

funcX[x_,y_]:= (x+y)^2

Regards,

Neil

POSTED BY: Neil Singer

Werner,

You can’t use setdelayed because we are intentionally altering the default evaluation order to get your function defined.

Other than the internal representation, the pure function and the delayed function behave identically. The only difference is the SetDelayed function undergoes an extra pattern match evaluation before it’s used.

In the documentation it states that these are equivalent.see Pure Functions Tutorial

Why is this subtle difference a problem in your application?

Regards

Neil

POSTED BY: Neil Singer
Posted 3 years ago

Thanks a lot, Neil

This is much better than ToExpression! And funcX is removed indeed.

I tried this before as well, but there is one problem: funcX is not defined as delayed set but is set immediately. You can see this if you do a "?funcX" after execution of your code. I added to your code a precedent funcX-definition (to see if it will be really removed), a Block and a final output of Information[funcX], funcX[a,b], funcX[3,2], funcX[a].

Remove[funcX];
funcX[z_] := Sin[z];(* Just for fun *)
ClearAll[a, b]

Block[{funcName, args, body, x, y},
  funcName = "funcX";
  args = {x, y};
  body = (x + y)^2;
  If[NameQ[funcName], Remove[Evaluate[funcName]]]; 
  Evaluate[Symbol[funcName]] = 
   Function[Evaluate[args], Evaluate[body]];
  ];

{Information[funcX], funcX[a, b], funcX[3, 2], funcX[a]}

enter image description here

Unfortunatly we cannot write ":=" instead of "=" in the definition of funcX. This will become: enter image description here

And we cannot write ":>" instead of "=". This will become: enter image description here

I tried several things like

Evaluate[Symbol[funcName]]=Function[Evaluate[args],Defer[Evaluate[body]]];

But this didn't suceed either.

Any idea to overcome this?

POSTED BY: Werner Geiger

This approach is also considerably faster than trying to construct a string and turning it into an expression (about 30 times faster).

POSTED BY: Neil Singer

Werner,

You can simplify things by using Function[]. I test if it is already defined, if so, I remove it so this code can be rerun without errors:

In[27]:= funcName = "funcX";
args = {x, y};
body = (x + y)^2;
If[NameQ[funcName], Remove[Evaluate[funcName]]];
Evaluate[Symbol[funcName]] = 
  Function[Evaluate[args], Evaluate[body]];
funcX[3, 2]

Out[33]= 25

Regards,

Neil

POSTED BY: Neil Singer
Posted 3 years ago

Hi Werner,

Here is a way to do it if args and body are converted to strings. Quite clumsy, not very generic, and probably easy to break.

ClearAll[makeFunction, funcX, x, y]

makeFunction[name_String, args_String, body_String] := ToExpression[name <> "[" <> args <> "]:= " <> body]

name = "funcX"; args = {x, y}; body = (x + y)^2;

argsString = ToString[#] <> "_" & /@ args // StringRiffle[#, ", "] &;
bodyString = TextString@body;

makeFunction[name, argsString, bodyString]

funcX[1, 2]
(* 9 *)
POSTED BY: Rohit Namjoshi
Posted 3 years ago

Great, thanks Rohit.

I just came by ToExpression. One needs to build a string "funcX[x_ ,y_ ] := <value of body>" and execute this string with ToExpression.

My program looks as follows. The only thing that does not yet work is to Remove or Clear funcX before the new definition.

Remove[funcX];
funcX[z_] := Sin[z];(* Just for fun *)
(*Echo[Information[funcX],"any old funcX: "];*)
Block[{funcName, args, body, func, funcDef, x, y},
 (* Given: *)
 funcName = "funcX";
 args = {x, y};
 body = (x + y)^2;
 (* func=funcX and funkDef = "funcX[x_,y_]" *)
 func = Symbol[funcName];
 funcDef = 
  StringRiffle[(ToString[#] <> "_") & /@ args, {funcName <> "[", ",", 
    "]"}];
 Echo[Grid[{
    {"funcName:", funcName // InputForm}, {"args:", args}, {"body:", 
     body}, {}, {"func:", func}, {"?func:", 
     Information[func]}, {"funcDef:", funcDef // InputForm}
    }, Frame -> True, Alignment -> {{Right, Left}}], 
  "values so far: "];
 (* Define function *)
 Remove[funcName];(* This doesn't work. Strange *)
 Clear[funcName];
 ToExpression[funcDef <> ":=" <> ToString[InputForm[body]]];
 Echo[Information[func], "?func after Clear and definition: "];
 ];
Echo[{Information[funcX], funcX[a, b]}, "the new funcX: "];

enter image description here

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