可选的命名参数,而不是将它们全部包装在"OptionValue"中

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它可能会一路走下去并且有一个语言构造来正确地进行命名参数,你可以通过他们的名字来引用它们.像命名参数的其他语言一样.(与此同时,我很好奇可行的解决办法......)

Bre*_*ion 6

为什么不使用类似的东西:

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)


Leo*_*rin 4

这是我答案的最终版本,包含 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执行时不应该具有全局值。