Wolfram mathematica 指定选项的设置

Wolfram mathematica 指定选项的设置,wolfram-mathematica,Wolfram Mathematica,如何明确说明选项的有效设置?就拿这个例子来说 Options[myFunc] = {opt1 -> "SomeString"}; myFunc[OptionsPattern[]] := Print[OptionValue[opt1]]; myFunc打印选项的值。如果我们计算myFunc[opt1->{1,2}],那么它将打印{1,2}。此函数基本上将打印您设置为opt1的任何内容。我的问题是,如何确保我的函数只接受给定数量的opt1值。我们可以从一些简单的东西开始,比如字符串和整数 为

如何明确说明选项的有效设置?就拿这个例子来说

Options[myFunc] = {opt1 -> "SomeString"};
myFunc[OptionsPattern[]] := Print[OptionValue[opt1]];
myFunc
打印选项的值。如果我们计算
myFunc[opt1->{1,2}]
,那么它将打印
{1,2}
。此函数基本上将打印您设置为
opt1
的任何内容。我的问题是,如何确保我的函数只接受给定数量的
opt1
值。我们可以从一些简单的东西开始,比如
字符串
整数

为了更好地了解在为
opt1
提供错误值时我们所期望的行为,我们可以看看在函数
Plot
中为
PlotRange
提供错误值时会发生什么

在图片中给出的示例中,我故意给
PlotRange
选项指定了错误的值,并给了我一条消息,为该特定选项指定了正确的值类型。似乎它
PlotRange
最终采用了它的默认值,因此它返回了
Graphics
对象

在这个简单的例子中,我们想要得到的是:

myFunc::sometag : Value of option opt1 -> `1` is not a string or integer.
如何实现这一点?

一个简单的解决方案 这里有一个简单的方法:

In[304]:= ClearAll[myFunc];
Options[myFunc] = {opt1 -> "SomeString"};
myFunc::badopt = "Value of option opt1 -> `1` is not a string or integer.";
myFunc[OptionsPattern[]] :=
   With[{val = OptionValue[opt1]},
      With[{error = ! MatchQ[val, _String | _Integer]},
         If[error, Message[myFunc::badopt , val]];
         (Print[val] /; ! error)]];
例如:

In[308]:= myFunc[opt1 -> 1]

During evaluation of In[308]:= 1

In[309]:= myFunc[opt1 -> {1, 2}]

During evaluation of In[309]:= myFunc::badopt: 
Value of option opt1 -> {1,2} is not a string or integer.

Out[309]= myFunc[opt1 -> {1, 2}]
使用自定义赋值运算符使其通用 我们可以利用函数内部的
OptionValue
与作为选项名的单个参数一起工作的事实,来消除错误检查的繁琐。这可以通过使用mma元编程工具实现。以下是自定义赋值运算符的代码:

ClearAll[def, OptionSpecs];
SetAttributes[def, HoldAll];
def[f_[args___] :> body_,OptionSpecs[optionSpecs : {(_ ->  {_, Fail :> _}) ..}]] :=
  f[args] :=
    Module[{error = False},
      Scan[
        With[{optptrn = First[# /. optionSpecs], optval = OptionValue[#]},
          If[! MatchQ[optval, optptrn ],
             error = True;
             Return[(Fail /. Last[# /. optionSpecs])[optval]]]] &, 
        optionSpecs[[All, 1]]
      ];
      body /; ! error];
ClearAll[myFunc1];
Options[myFunc1] = {opt1 -> "SomeString", opt2 -> 0};
myFunc1::badopt1 = "Value of option opt1 -> `1` is not a string or integer.";
myFunc1::badopt2 =  "Value of option opt2 -> `1` is not an integer.";
def[myFunc1[OptionsPattern[]] :> 
       Print[{OptionValue[opt1], OptionValue[opt2]}],
    OptionSpecs[{
       opt1 -> {_Integer | _String, 
           Fail :> ((Message[myFunc1::badopt1, #]; Return[$Failed]) &)},
       opt2 -> {_Integer,
           Fail :> ((Message[myFunc1::badopt2, #]; Return[$Failed]) &)}}
]];
它所做的是将函数定义作为一个规则
f_[args_uuuuuuu]:>body_uu
,并且 可接受选项设置的规范,以及在检测到其中一个已传递选项中的错误时要执行的操作。然后,我们在执行主体之前注入错误测试代码(
Scan
)。一旦发现设置不正确的第一个选项,错误标志将设置为
True
,并且在该选项规范的
Fail:>code\u
部分中指定了任何代码。选项规范模式
{u,Fail:>})应为
(optname{optpattern,Fail:>onerror})
,其中
optname
是选项名称,
optpattern
是选项值必须匹配的模式,
onerror
是检测到错误时要执行的任意代码。请注意,我们在
Fail:>onerror
中使用
RuleDelayed
,以防止过早评估该代码。注意b.t.w.
OptionSpecs
wrapper仅为可读性而添加-它是一个完全空闲的符号,没有附加任何规则

以下是使用此自定义赋值运算符定义的函数示例:

ClearAll[def, OptionSpecs];
SetAttributes[def, HoldAll];
def[f_[args___] :> body_,OptionSpecs[optionSpecs : {(_ ->  {_, Fail :> _}) ..}]] :=
  f[args] :=
    Module[{error = False},
      Scan[
        With[{optptrn = First[# /. optionSpecs], optval = OptionValue[#]},
          If[! MatchQ[optval, optptrn ],
             error = True;
             Return[(Fail /. Last[# /. optionSpecs])[optval]]]] &, 
        optionSpecs[[All, 1]]
      ];
      body /; ! error];
ClearAll[myFunc1];
Options[myFunc1] = {opt1 -> "SomeString", opt2 -> 0};
myFunc1::badopt1 = "Value of option opt1 -> `1` is not a string or integer.";
myFunc1::badopt2 =  "Value of option opt2 -> `1` is not an integer.";
def[myFunc1[OptionsPattern[]] :> 
       Print[{OptionValue[opt1], OptionValue[opt2]}],
    OptionSpecs[{
       opt1 -> {_Integer | _String, 
           Fail :> ((Message[myFunc1::badopt1, #]; Return[$Failed]) &)},
       opt2 -> {_Integer,
           Fail :> ((Message[myFunc1::badopt2, #]; Return[$Failed]) &)}}
]];
以下是使用示例:

In[473]:= myFunc1[]
During evaluation of In[473]:= {SomeString,0}

In[474]:= myFunc1[opt2-> 10]
During evaluation of In[474]:= {SomeString,10}

In[475]:= myFunc1[opt2-> 10,opt1-> "other"]
During evaluation of In[475]:= {other,10}

In[476]:= myFunc1[opt2-> 1/2]
During evaluation of In[476]:= myFunc1::badopt2: 
Value of option opt2 -> 1/2 is not an integer.

Out[476]= $Failed

In[477]:= myFunc1[opt2-> 15,opt1->1/2]
During evaluation of In[477]:= myFunc1::badopt1: 
Value of option opt1 -> 1/2 is not a string or integer.

Out[477]= $Failed
自动向已定义的函数添加选项检查 您可能还对我为测试传递的选项而编写的包感兴趣:
CheckOptions
,可用。该软件包附带一个说明其用途的笔记本。它解析函数的定义并创建其他定义以检查选项。当前的缺点(除了可能并不总是合适的新定义的产生)是,它只包括通过
OptionQ
谓词定义选项的旧方法(我还没有更新它以涵盖
OptionValue-OptionsPattern
。我将在这里复制随附笔记本的一部分,以说明它是如何工作的:

考虑一个模型函数:

In[276]:= ClearAll[f];
f[x_, opts___?OptionQ]:= x^2;
f[x_, y_, opts___?OptionQ] := x + y;
f[x_, y_, z_] := x*y*z;
In[280]:= 
f::badopt="Inappropriate option";
test[f,heldopts_Hold,heldArgs_Hold]:=(FontSize/.Flatten[List@@heldopts])=!=FontSize;
rhsF[f,__]:=(Message[f::badopt];$Failed);
假设我们希望在将选项
FontSize
传递给函数时返回错误消息:

In[276]:= ClearAll[f];
f[x_, opts___?OptionQ]:= x^2;
f[x_, y_, opts___?OptionQ] := x + y;
f[x_, y_, z_] := x*y*z;
In[280]:= 
f::badopt="Inappropriate option";
test[f,heldopts_Hold,heldArgs_Hold]:=(FontSize/.Flatten[List@@heldopts])=!=FontSize;
rhsF[f,__]:=(Message[f::badopt];$Failed);
我们添加了选项-检查定义:

In[283]:= AddOptionsCheck[f,test,rhsF]
Out[283]= {HoldPattern[f[x_,opts___?OptionQ]/;test[f,Hold[opts],Hold[x,opts]]]:>
                rhsF[f,Hold[opts],Hold[x,opts]],
           HoldPattern[f[x_,y_,opts___?OptionQ]/;test[f,Hold[opts],Hold[x,y,opts]]]:>
                rhsF[f,Hold[opts],Hold[x,y,opts]],
           HoldPattern[f[x_,opts___?OptionQ]]:>x^2,
           HoldPattern[f[x_,y_,opts___?OptionQ]]:>x+y,
           HoldPattern[f[x_,y_,z_]]:>x y z}
如您所见,一旦我们调用
addoptions检查
,它就会生成新的定义。它接受函数名、测试函数和失败时要执行的函数。测试函数接受主函数名、传递给它的选项(包装在
Hold
中)以及传递给它的非选项参数(也包装在
Hold
)中。从生成的定义中,您可以看到is的作用

我们现在检查各种输入:

In[284]:= f[3]
Out[284]= 9

In[285]:= f[3,FontWeight->Bold]
Out[285]= 9

In[286]:= f[3,FontWeight->Bold,FontSize->5]
During evaluation of In[286]:= f::badopt: Inappropriate option
Out[286]= $Failed

In[289]:= f[a,b]
Out[289]= a+b

In[290]:= f[a,b,FontWeight->Bold]
Out[290]= a+b

In[291]:= f[a,b,FontWeight->Bold,FontSize->5]
During evaluation of In[291]:= f::badopt: Inappropriate option
Out[291]= $Failed

In[292]:= OptionIsChecked[f,test]
Out[292]= True
请注意,测试函数可以测试涉及函数名、传递的参数和传递的选项的任意条件。我的另一个软件包,
PackageOptionChecks
,位于同一页面,它有一个更简单的语法,可以专门测试选项的r.h.s.,也可以应用于整个软件包。实践l它的使用示例是另一个软件包,
PackageSymbolsDependencies
,它的函数选项受
PackageOptionChecks
的“保护”。此外,
PackageOptionChecks
也可以应用于
Global'
上下文中的函数,不需要有软件包


当前执行的另一个限制是我们不能返回未被评估的函数。请参阅笔记本中附带的一个更详细的讨论。如果有足够的兴趣对此进行讨论,我会考虑更新包来删除我提到的一些限制。

好包。真的是MM吗?a没有任何内置的机制来实现这一点?@acl据我所知不是这样(这并不意味着没有这样的内置机制,但至少它没有很好的文档记录).OTOH,内置函数显然使用了某种机制来检查。我不知道这种机制是否是从每个特定函数的细节中考虑出来的。正如我在其他地方提到的,传递不适当的选项设置所导致的错误是我遇到过的最糟糕的错误之一,而与它们的斗争就是原因所在让我来写这两个包。