开发者

Hold any argument

Is it possible to define a function that holds arguments at given positions ?

Or to do something like HoldLast 开发者_如何转开发as a counterpart to HoldFirst ?


As far as I know, you can not do this directly in the sense that there isn't a HoldN attribute. However, below there is a work-around that should be doing what you requested.


Proposed solution

One simple way is to define an auxiliary function that will do the main work, and your "main" function (the one that will actually be called) as HoldAll, like so:

In[437]:= 
SetAttributes[f, HoldAll];
f[a_, b_, c_] :=
   faux[a, Unevaluated[b], c];
faux[a_, b_, c_] := Hold[a, b, c]

In[440]:= f[1^2, 2^2, 3^2]
Out[440]= Hold[1, 2^2, 9] 

You don't have to expose the faux to the top level, can wrap everyting in Module[{faux}, your definitions] instead.


Automation through meta-programming

This procedure can be automated. Here is a simplistic parser for the function signatures, to extract pattern names (note - it is indeed simplistic):

splitHeldSequence[Hold[seq___], f_: Hold] := List @@ Map[f, Hold[seq]];

getFunArguments[Verbatim[HoldPattern][Verbatim[Condition][f_[args___], test_]]] := 
     getFunArguments[HoldPattern[f[args]]];

getFunArguments[Verbatim[HoldPattern][f_[args___]]] := 
     FunArguments[FName[f], FArgs @@ splitHeldSequence[Hold[args]]];

(*This is a simplistic "parser".It may miss some less trivial cases*)

getArgumentNames[args__FArgs] := 
   args //. {
     Verbatim[Pattern][tag_, ___] :> tag, 
     Verbatim[Condition][z_, _] :> z, 
     Verbatim[PatternTest][z_, _] :> z
   };

Using this, we can write the following custom definition operator:

ClearAll[defHoldN];
SetAttributes[defHoldN, HoldFirst];
defHoldN[SetDelayed[f_[args___], rhs_], n_Integer] :=
   Module[{faux},
      SetAttributes[f, HoldAll];
      With[{heldArgs = 
         MapAt[
            Unevaluated,
            Join @@ getArgumentNames[getFunArguments[HoldPattern[f[args]]][[2]]],
            n]
         },
        SetDelayed @@ Hold[f[args], faux @@ heldArgs];
        faux[args] := rhs]]

This will analyze your original definition, extract pattern names, wrap the argument of interest in Unevaluated, introduce local faux, and make a 2-step definition - basically the steps we did manually. We need SetDelayed @@ .. to fool the variable renaming mechanism of With, so that it won't rename our pattern variables on the l.h.s. Example:

In[462]:= 
ClearAll[ff];
defHoldN[ff[x_,y_,z_]:=Hold[x,y,z],2]

In[464]:= ?ff
Global`ff
Attributes[ff]={HoldAll}

ff[x_,y_,z_]:=faux$19106@@Hold[x,Unevaluated[y],z]

In[465]:= ff[1^2,2^2,3^2]
Out[465]= Hold[1,2^2,9]

Notes

Note that this is trivial to generalize to a list of positions in which you need to hold the arguments. In general, you'd need a better pattern parser, but the simple one above may be a good start. Note also that there will be a bit of run-time overhead induced with this construction, and also that the Module-generated auxiliary functions faux won't be garbage-collected when you Clear or Remove the main ones - you may need to introduce a special destructor for your functions generated with defHoldN. For an alternative take on this problem, see my post in this thread (the one where I introduced the makeHoldN function).


Another way to do would be for example:

SetAttributes[f,HoldFirst];
f[{heldArg1_,heldArg2_},arg3_,arg4_,arg5_]:= Hold[arg3 heldArg1];

And this would allow to be able to have any mix of held and non held arguments. HoldRest could be used similarly.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜