是否可以定义一个在给定位置保存参数的函数?
或者像HoldLast一样做HoldFirst的对手?
答案 0 :(得分:15)
据我所知,在没有HoldN
属性的意义上,你不能直接这样做。
但是,下面有一个解决办法应该是你要求的。
一种简单的方法是定义一个辅助函数来完成主要工作,将你的“main”函数(实际调用的函数)定义为HoldAll
,如下所示:
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]
您不必将faux
公开到顶层,而是可以将Module[{faux}, your definitions]
中的每一个包裹起来。
此程序可以自动化。这是一个简单的函数签名解析器,用于提取模式名称(注意 - 它确实简单化):
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
};
使用它,我们可以编写以下自定义定义运算符:
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]]
这将分析您的原始定义,提取模式名称,将感兴趣的参数包装在Unevaluated
中,引入本地faux
,并进行两步定义 - 基本上是我们手动执行的步骤。我们需要SetDelayed @@ ..
来欺骗With
的变量重命名机制,以便它不会重命名l.h.s.上的模式变量。例如:
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]
请注意,这可以简化为需要保存参数的位置列表。一般来说,你需要一个更好的模式解析器,但上面简单的解析器可能是一个好的开始。另请注意,此构造会导致一些运行时开销,并且Module
生成的辅助函数faux
在Clear
时不会被垃圾收集或Remove
主要的 - 您可能需要为使用defHoldN
生成的函数引入一个特殊的析构函数。有关此问题的替代方法,请参阅this主题中的帖子(我引入makeHoldN
函数的帖子)。
答案 1 :(得分:1)
另一种方法是例如:
SetAttributes[f,HoldFirst];
f[{heldArg1_,heldArg2_},arg3_,arg4_,arg5_]:= Hold[arg3 heldArg1];
这将允许任何持有和非持有参数的混合。 HoldRest可以类似地使用。