虽然有几个答案强调了使用选项的旧方式和新方式的不同方面,但我想提出一些额外的意见。较新的构造OptionValue
-OptionsPattern
提供比 , 更多的安全性OptionQ
,因为OptionValue
检查全局选项列表以确保函数知道传递的选项。然而,旧的OptionQ
似乎更容易理解,因为它仅基于标准模式匹配,并且与任何全局属性没有直接关系。您是否想要这些结构中的任何一个提供的这种额外安全性取决于您,但我猜大多数人觉得它很有用,尤其是对于大型项目。
这些类型检查非常有用的一个原因是,通常选项作为参数由函数以链式方式、过滤等方式传递,因此如果没有此类检查,某些模式匹配错误将很难捕获,因为它们会在“远离”其原产地的地方造成伤害。
就核心语言而言,OptionValue
-OptionsPattern
结构是模式匹配器的补充,也许是其所有功能中最“神奇”的。语义上没有必要,只要愿意将选项视为规则的一种特殊情况。此外,OptionValue
将模式匹配连接到Options[symbol]
- 一个全局属性。因此,如果一个人坚持语言纯度,那么规则opts___?OptionQ
似乎更容易理解——除了标准的规则替换语义之外,不需要任何东西来理解这一点:
f[a_, b_, opts___?OptionQ] := Print[someOption/.Flatten[{opts}]/.Options[f]]
(我提醒一下,OptionQ
谓词是专门为识别旧版本 Mathematica 中的选项而设计的),而这个:
f[a_, b_, opts:OptionsPattern[]] := Print[OptionValue[someOption]]
看起来很神奇。Trace
当您使用并看到简短形式的计算结果为较长形式时,它会变得更加清晰OptionValue
,但是它自动确定封闭函数名称的事实仍然值得注意。
OptionsPattern
成为模式语言的一部分还有更多的后果。一是@Sasha 讨论的速度改进。然而,速度问题经常被过分强调(这并不是为了减损他的观察),我希望这对于带有选项的函数尤其如此,因为这些往往是更高级别的函数,可能没有琐碎的主体,大部分计算时间都将花费在其中。
另一个相当有趣的区别是何时需要将选项传递给保存其参数的函数。考虑以下示例:
ClearAll[f, ff, fff, a, b, c, d];
Options[f] = Options[ff] = {a -> 0, c -> 0};
SetAttributes[{f, ff}, HoldAll];
f[x_, y_, opts___?OptionQ] :=
{{"Parameters:", {HoldForm[x], HoldForm[y]}}, {" options: ", {opts}}};
ff[x_, y_, opts : OptionsPattern[]] :=
{{"Parameters:", {HoldForm[x], HoldForm[y]}}, {" options: ", {opts}}};
还行吧:
In[199]:= f[Print["*"],Print["**"],a->b,c->d]
Out[199]= {{Parameters:,{Print[*],Print[**]}},{ options: ,{a->b,c->d}}}
但是在这里,我们OptionQ
基于 - 的函数将评估作为模式匹配过程的一部分泄漏:
In[200]:= f[Print["*"],Print["**"],Print["***"],a->b,c->d]
During evaluation of In[200]:= ***
Out[200]= f[Print[*],Print[**],Print[***],a->b,c->d]
这并非完全无关紧要。发生的情况是,模式匹配器为了确定匹配或不匹配的事实,必须评估第三个Print
,作为评估的一部分OptionQ
,因为OptionQ
不包含参数。为避免评估泄漏,需要使用Function[opt,OptionQ[Unevaluated[opt]],HoldAll]
. 我们没有这个问题,因为匹配的事实可以纯粹从句法上建立OptionQ
:OptionsPattern
In[201]:= ff[Print["*"],Print["**"],a->b,c->d]
Out[201]= {{Parameters:,{Print[*],Print[**]}},{ options: ,{a->b,c->d}}}
In[202]:= ff[Print["*"],Print["**"],Print["***"],a->b,c->d]
Out[202]= ff[Print[*],Print[**],Print[***],a->b,c->d]
所以,总结一下:我认为选择一种方法而不是另一种方法很大程度上是一个品味问题——每一种方法都可以有效地使用,而且每一种方法都可以被滥用。我更倾向于使用更新的方法,因为它提供了更多的安全性,但我不排除存在一些会让你感到惊讶的极端情况——而旧方法在语义上更容易理解。这类似于 C-C++ 比较(如果这是一个合适的比较):自动化和(可能)安全性与简单性和纯度。我的两分钱。