How to define a function dynamically?

Posted 11 months ago
2683 Views
|
11 Replies
|
9 Total Likes
|
 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?
11 Replies
Sort By:
Posted 10 months 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 10 months ago
 Thanks a lot, Neil!This works perfectly.Regards (Sorry for being late)Werner
Posted 11 months ago
 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 11 months ago
 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 11 months ago
 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 11 months ago
 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 TutorialWhy is this subtle difference a problem in your application? RegardsNeil
Posted 11 months ago
 Thanks a lot, NeilThis 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]} Unfortunatly we cannot write ":=" instead of "=" in the definition of funcX. This will become: And we cannot write ":>" instead of "=". This will become: 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 11 months ago
 This approach is also considerably faster than trying to construct a string and turning it into an expression (about 30 times faster).
Posted 11 months ago
 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
 Great, thanks Rohit.I just came by ToExpression. One needs to build a string "funcX[x_ ,y_ ] := " 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: "]; 
 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 *)