旧习惯很难改掉,我意识到我一直在使用opts___Rule模式匹配以及在我目前正在开发的超大型程序包中使用thisoption /. {opts} /. Options[myfunction]之类的结构。萨尔·曼南戈(Sal Manango)的“Mathematica Cookbook”使我想起,版本6之后的方法是opts:OptionsPattern[]OptionValue[thisoption]。该软件包仍然需要版本8,但多年来,我从未改变过编写此类代码的方式。

是否值得将我的版本6之前的所有方式重构?有绩效或其他好处吗?

问候

韦贝亚

编辑:摘要

回答这个问题有很多好处,所以谢谢(当然还要加一)。总而言之,是的,我应该重构为使用OptionsPatternOptionValue。 (注意:OptionsPattern不像以前那样是OptionPattern!)原因有很多:

  • 触摸速度更快(@Sasha)
  • 它可以更好地处理参数必须位于HoldForm(@Leonid)中的函数
  • OptionsPattern自动检查您是否向该函数传递了有效选项(如果要传递给其他函数(@Leonid),则仍需要FilterRules)
  • 它更好地处理RuleDelayed(:>)(@rcollyer)
  • 无需使用Flatten(@Andrew)即可处理规则的嵌套列表
  • 使用OptionValue /@ list分配多个局部变量要容易一些,而不是多次调用someoptions /. {opts} /. Options[thisfunction](在@rcollyer和我之间的评论中出现)

  • 编辑:7月25日我最初认为一次使用/.语法可能仍然有意义,那就是如果您是故意从另一个函数中提取默认选项,而不是实际调用的那个。事实证明,这是通过使用OptionsPattern[]的形式进行处理的,其中包括一个头部列表,例如:OptionsPattern[{myLineGraph, DateListPlot, myDateTicks, GraphNotesGrid}](请参见documentation中的“更多信息”部分)。我最近才解决这个问题。

    最佳答案

    尽管有几个答案强调了使用选项的旧方法与新方法的不同方面,但我想作一些补充说明。较新的结构OptionValue-OptionsPatternOptionQ提供更高的安全性,因为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讨论的速度改进。但是,速度问题常常被过分强调(这不会影响他的观察结果),我希望这对于带有选项的功能尤为重要,因为这些功能往往是更高级别的功能,可能具有非功能性。琐碎的 body ,大部分的计算时间将用在这里。

    另一个相当有趣的区别是,当需要将选项传递给保留其参数的函数时。考虑以下示例:
    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++比较(如果合适的话):自动化和(可能)安全性与简单性和纯度的比较。我的两分钱。

    10-05 21:47