dre*_*ves 4 language-features wolfram-mathematica
假设我有一个带有可选命名参数的函数,但我坚持用它们的未加工名称来引用这些参数.
考虑这个函数,它添加了两个命名参数a和b:
Options[f] = {a->0, b->0}; (* The default values. *)
f[OptionsPattern[]] :=
OptionValue[a] + OptionValue[b]
Run Code Online (Sandbox Code Playgroud)
如何编写该函数的一个版本,其中最后一行被简单替换a+b?(想象一下,这a+b是一大堆代码.)
以下问题的答案显示了如何缩写OptionValue(说起来容易做起来),而不是如何完全摆脱它:Mathematica中的可选命名参数
哲学附录:似乎Mathematica将会拥有这种神奇的东西,OptionsPattern并且OptionValue它可能会一路走下去并且有一个语言构造来正确地进行命名参数,你可以通过他们的名字来引用它们.像命名参数的其他语言一样.(与此同时,我很好奇可行的解决办法......)
为什么不使用类似的东西:
Options[f] = {a->0, b->0};
f[args___] := (a+b) /. Flatten[{args, Options[f]}]
Run Code Online (Sandbox Code Playgroud)
对于更复杂的代码,我可能会使用类似的东西:
Options[f] = {a->0, b->0};
f[OptionsPattern[]] := Block[{a,b}, {a,b} = OptionValue[{a,b}]; a+b]
Run Code Online (Sandbox Code Playgroud)
并使用对OptionValue的单次调用来一次获取所有值.(主要原因是如果存在未知选项,这会减少消息.)
更新,以编程方式从选项列表生成变量:
Options[f] = {a -> 0, b -> 0};
f[OptionsPattern[]] :=
With[{names = Options[f][[All, 1]]},
Block[names, names = OptionValue[names]; a + b]]
Run Code Online (Sandbox Code Playgroud)
这是我答案的最终版本,包含 Brett Champion 答案的贡献。
ClearAll[def];
SetAttributes[def, HoldAll];
def[lhs : f_[args___] :> rhs_] /; !FreeQ[Unevaluated[lhs], OptionsPattern] :=
With[{optionNames = Options[f][[All, 1]]},
lhs := Block[optionNames, optionNames = OptionValue[optionNames]; rhs]];
def[lhs : f_[args___] :> rhs_] := lhs := rhs;
Run Code Online (Sandbox Code Playgroud)
之所以在参数中将定义作为延迟规则给出,是因为这样我们就可以从语法突出显示中受益。使用块技巧是因为它适合解决问题:它不会干扰函数内可能的嵌套词法作用域构造,因此不存在意外捕获变量的危险。我们检查 OptionsPattern 是否存在,因为如果没有它,该代码对于定义将不正确,并且我们def也希望在这种情况下工作。使用示例:
Clear[f, a, b, c, d];
Options[f] = {a -> c, b -> d};
(*The default values.*)
def[f[n_, OptionsPattern[]] :> (a + b)^n]
Run Code Online (Sandbox Code Playgroud)
您现在可以查看定义:
Global`f
f[n$_,OptionsPattern[]]:=Block[{a,b},{a,b}=OptionValue[{a,b}];(a+b)^n$]
f[n_,m_]:=m+n
Options[f]={a->c,b->d}
Run Code Online (Sandbox Code Playgroud)
我们现在可以测试一下:
In[10]:= f[2]
Out[10]= (c+d)^2
In[11]:= f[2,a->e,b->q]
Out[11]= (e+q)^2
Run Code Online (Sandbox Code Playgroud)
修改是在“编译时”完成的,并且非常透明。虽然此解决方案节省了 Brett 的一些输入,但它在“编译时”确定选项名称集,而 Brett 则在“运行时”确定选项名称集。因此,它比 Brett 的更脆弱:如果在用 定义函数后向该函数添加一些新选项def,则必须清除它并重新运行def。然而,在实践中,习惯上从 ClearAll 开始并将所有定义放在一个块(单元格)中,因此这似乎不是一个真正的问题。此外,它不能使用字符串选项名称,但您最初的概念也假设它们是符号。此外,它们不应该具有全局值,至少在def执行时不应该具有全局值。