Ver*_*eia 23 wolfram-mathematica mathematica-8
老习惯很难,我意识到我一直在使用opts___Rule模式匹配和构造,就像thisoption /. {opts} /. Options[myfunction]我正在开发的非常大的包中一样.Sal Manango的"Mathematica Cookbook"让我想起,版本6后的做法是opts:OptionsPattern[]和OptionValue[thisoption].无论如何,该软件包需要版本8,但我从未改变过多年来编写此类代码的方式.
是否值得从我的pre-version-6做事方式重构所有这些?是否有表现或其他好处?
问候
维尔比亚
编辑:摘要
为回答这个问题,我们提出了很多好处,所以谢谢你们(当然还有一个).总结一下,是的,我应该重构使用OptionsPattern和OptionValue.(注意:OptionsPattern不像OptionPattern以前那样!)原因有很多:
HoldForm(@Leonid)OptionsPattern自动检查您是否将有效选项传递给该函数(FilterRules如果要传递给其他函数,仍然需要它(@Leonid)RuleDelayed(:>)更好(@rcollyer)Flatten(@Andrew)OptionValue /@ list而不是多次调用来分配多个局部变量更容易一些someoptions /. {opts} /. Options[thisfunction](在@rcollyer和我之间的评论中提出)编辑:7月25日我最初认为使用/.语法的一次可能仍然有意义,如果你故意从另一个函数中提取默认选项,而不是实际调用的那个.事实证明,这是通过使用其中OptionsPattern[]包含头部列表的形式来处理的,例如:( OptionsPattern[{myLineGraph, DateListPlot, myDateTicks, GraphNotesGrid}]请参阅文档中的"更多信息"部分).我最近才解决这个问题.
Sas*_*sha 13
看起来依赖于模式匹配器产生比使用更快的执行,PatternTest因为后者需要调用评估器.无论如何,我的时间表明可以实现一些加速,但我不认为它们如此重要以至于促使重新分解.
In[7]:= f[x__, opts : OptionsPattern[NIntegrate]] := {x,
OptionValue[WorkingPrecision]}
In[8]:= f2[x__, opts___?OptionQ] := {x,
WorkingPrecision /. {opts} /. Options[NIntegrate]}
In[9]:= AbsoluteTiming[Do[f[1, 2, PrecisionGoal -> 17], {10^6}];]
Out[9]= {5.0885088, Null}
In[10]:= AbsoluteTiming[Do[f2[1, 2, PrecisionGoal -> 17], {10^6}];]
Out[10]= {8.0908090, Null}
In[11]:= f[1, 2, PrecisionGoal -> 17]
Out[11]= {1, 2, MachinePrecision}
In[12]:= f2[1, 2, PrecisionGoal -> 17]
Out[12]= {1, 2, MachinePrecision}
Run Code Online (Sandbox Code Playgroud)
And*_*lan 11
一个鲜为人知(但经常有用)的事实是允许选项出现在嵌套列表中:
In[1]:= MatchQ[{{a -> b}, c -> d}, OptionsPattern[]]
Out[1]= True
Run Code Online (Sandbox Code Playgroud)
FilterRules等选项处理函数知道这一点:
In[2]:= FilterRules[{{PlotRange -> 3}, PlotStyle -> Blue,
MaxIterations -> 5}, Options[Plot]]
Out[2]= {PlotRange -> 3, PlotStyle -> RGBColor[0, 0, 1]}
Run Code Online (Sandbox Code Playgroud)
OptionValue将其考虑在内:
In[3]:= OptionValue[{{a -> b}, c -> d}, a]
Out[3]= b
Run Code Online (Sandbox Code Playgroud)
但是,ReplaceAll(/.)当然没有考虑到这一点:
In[4]:= a /. {{a -> b}, c -> d}
During evaluation of In[4]:= ReplaceAll::rmix: Elements of {{a->b},c->d} are a mixture of lists and nonlists. >>
Out[4]= a /. {{a -> b}, c -> d}
Run Code Online (Sandbox Code Playgroud)
因此,如果您使用OptionsPattern,您可能还应该使用OptionValue来确保您可以使用用户传递的选项集.
另一方面,如果你使用ReplaceAll(/.),你应该坚持opts___Rule出于同样的原因.
请注意,opts___Rule在某些(公认的模糊)案例中,这也有点过于宽容:
不是一个有效的选择:
In[5]:= MatchQ[Unevaluated[Rule[a]], OptionsPattern[]]
Out[5]= False
Run Code Online (Sandbox Code Playgroud)
但是___Rule让它通过:
In[6]:= MatchQ[Unevaluated[Rule[a]], ___Rule]
Out[6]= True
Run Code Online (Sandbox Code Playgroud)
更新:正如rcollyer所指出的,另一个更严重的问题___Rule是它错过了使用RuleDelayed(:>)指定的选项.你可以解决它(参见rcollyer的回答),但这是使用OptionValue的另一个好理由.
Leo*_*rin 11
虽然有几个答案强调了使用选项的旧方式与新方式的不同方面,但我还是想做一些额外的观察.较新的构造OptionValue- OptionsPattern 提供更多的安全性OptionQ,因为OptionValue检查全局选项列表以确保函数已知传递的选项.OptionQ然而,旧版似乎更容易理解,因为它仅基于标准模式匹配,并且不与任何全局属性直接相关.您是否希望这些构造提供的额外安全性取决于您,但我的猜测是大多数人认为它很有用,特别是对于大型项目.
这些类型检查真正有用的一个原因是,选项通过函数以类似链的方式作为参数传递,过滤等等,因此如果没有这样的检查,一些模式匹配错误将很难捕获,因为它们会对他们原籍地"远离"造成伤害.
就核心语言而言,OptionValue- OptionsPattern构造是模式匹配器的补充,也许是其所有特征中最"神奇"的.只要人们愿意将选项视为规则的特例,就没有必要在语义上.此外,OptionValue将模式匹配连接到Options[symbol]- 全局属性.因此,如果一个人坚持语言纯度,那么规则opts___?OptionQ似乎更容易理解 - 除了标准规则替换语义之外,不需要任何其他东西来理解这一点:
f[a_, b_, opts___?OptionQ] := Print[someOption/.Flatten[{opts}]/.Options[f]]
Run Code Online (Sandbox Code Playgroud)
(我提醒说OptionQ谓词是专门为识别旧版Mathematica中的选项而设计的),而这个:
f[a_, b_, opts:OptionsPattern[]] := Print[OptionValue[someOption]]
Run Code Online (Sandbox Code Playgroud)
看起来很神奇.当你使用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}}};
Run Code Online (Sandbox Code Playgroud)
还行吧:
In[199]:= f[Print["*"],Print["**"],a->b,c->d]
Out[199]= {{Parameters:,{Print[*],Print[**]}},{ options: ,{a->b,c->d}}}
Run Code Online (Sandbox Code Playgroud)
但是这里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]
Run Code Online (Sandbox Code Playgroud)
这并非完全无足轻重.所发生的是,模式匹配器,为了建立匹配或不匹配的事实,必须评估第三个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]
Run Code Online (Sandbox Code Playgroud)
因此,总结一下:我认为选择一种方法而不是另一种方法主要是品味问题 - 每种方法都可以有效地使用,而且每种方法都可以被滥用.我更倾向于使用更新的方式,因为它提供了更多的安全性,但我不排除存在一些令人惊讶的角落情况 - 而旧的方法在语义上更容易理解.这类似于C-C++比较(如果这是合适的):自动化和(可能)安全性与简单性和纯度.我的两分钱.
你的代码本身有一个微妙但可修复的缺陷.该模式opts___Rule与表单的选项不匹配a :> b,因此如果您需要使用它,则必须更新代码.最直接的解决办法是更换opts___Rule有opts:(___Rule | ___RuleDelayed)需要超过打字OptionsPattern[].但是,对于我们中间的懒惰,OptionValue[...]需要更多的打字而不是简短的形式ReplaceAll.但是,我认为它使得阅读代码更加清晰.
我发现使用OptionsPattern[]和OptionValue更容易阅读并立即理解正在做的事情.较旧的形式,opts___ ...并且ReplaceAll在第一遍读完时难以理解.除此之外,明确的时间优势,我会更新您的代码.