当前位置:  开发笔记 > 编程语言 > 正文

防止Mathematica中的运行时错误雪崩

如何解决《防止Mathematica中的运行时错误雪崩》经验,为你挑选了2个好方法。

当笔记本超出一些功能时我遇到的一个典型情况 - 我评估一个表达式,但是我得到了Beep而不是正确的答案,然后是几十个无用的警告,接着是"进一步输出...将被抑制"

我觉得有用的一件事 - 在函数内部使用类似Python的"断言"来强制内部一致性.还有其他提示吗?

Assert[expr_, msg_] := If[Not[expr], Print[msg]; Abort[], None]

编辑11/14 警告雪崩的一般原因是子表达式评估为"坏"值.这会导致父表达式计算为"坏"值,并且此"不良"会一直传播到根目录.内置评估一路上注意到不良并产生警告."坏"可能意味着表达错误的头部,列表中的元素数量错误,负的确定矩阵而不是正定义等等.一般来说,它不适合父表达式的语义.

解决这个问题的一种方法是重新定义所有函数,以便在"输入错误"时返回未评估的值.这将处理内置函数生成的大多数消息.执行像"Part"这样的结构操作的内置函数仍将尝试评估您的值并可能产生警告.

将调试器设置为"中断消息"可以防止出现大量错误,尽管将其一直打开似乎有点过头了



1> rcollyer..:

正如其他人所指出的,有三种方法可以一致的方式处理错误:

    正确输入参数并设置运行函数的条件,

    正确和一致地处理产生的错误,以及

    简化您应用这些步骤的方法.

正如Samsdram指出的那样,正确输入你的功能将有很大帮助.不要忘记:形式,Pattern因为有时更容易以这种形式表达某些模式,例如x:{{_, _} ..}.显然,当这还不够时,PatternTests(?)和Conditions(/;)是要走的路.Samdram很好地介绍了这一点,但我想补充一点,你可以通过纯函数创建自己的模式测试,例如f[x_?(Head[#]===List&)]相当于f[x_List].注意,在使用纯函数的&符号形式时,括号是必需的.

处理生成错误的最简单方法显然是Off或更本地化Quiet.在大多数情况下,我们都同意完全关闭我们不想要的消息是一个坏主意,但是Quiet当你知道你正在做一些会引起投诉的事情时,它会非常有用,但在其他方面是正确的.

ThrowCatch拥有自己的位置,但我觉得它们只应在内部使用,而您的代码应通过Message设施传达错误.可以使用与设置使用消息相同的方式创建消息.我认为,关键是一个连贯的错误策略可以使用函数构建Check,CheckAbort,AbortProtect.

我的代码中的一个示例是OpenAndRead防止在中止读取操作时保留开放流,如下所示:

OpenAndRead[file_String, fcn_]:=
Module[{strm, res},
  strm = OpenRead[file];
  res = CheckAbort[ fcn[strm], $Aborted ];
  Close[strm];
  If[res === $Aborted, Abort[], res] (* Edited to allow Abort to propagate *)
]

直到最近,才有用

fcn[ file_String,  ] := OpenAndRead[file, fcn[#, ]&]
fcn[ file_InputStream,  ] := 

但是,每次都这样做很烦人.

这是belisarius解决方案发挥作用的地方,通过创建一个可以一致使用的方法.不幸的是,他的解决方案有一个致命的缺陷:你失去了语法高亮设施的支持.所以,这是我OpenAndRead从上面想到的一个替代方案

MakeCheckedReader /: 
    SetDelayed[MakeCheckedReader[fcn_Symbol, symbols___], a_] :=
    Quiet[(fcn[file_String, symbols] := OpenAndRead[file, fcn[#, symbols] &];
           fcn[file_Symbol, symbols] := a), {RuleDelayed::"rhs"}]

有用处

MakeCheckedReader[ myReader, a_, b_ ] := {file$, a, b} (*as an example*)

现在,检查定义会myReader给出两个定义,就像我们想要的那样.但是,在函数体中,file必须称为file$.(我还没想出如何根据我的意愿命名文件var.)

编辑: MakeCheckedReader通过不实际做任何事情来工作.相反,TagSet(/:)规范告诉Mathematica,当MakeCheckedReader在LHS上找到a时SetDelayed,用所需的函数定义替换它.另外,请注意使用Quiet; 否则,它会抱怨模式a_b_出现在等式的右侧.

编辑2:库奇马指出,如何能够使用file没有file$定义检查时,读卡器.更新的解决方案如下:

MakeCheckedReader /: 
    SetDelayed[MakeCheckedReader[fcn_Symbol, symbols___], a_] :=
    Quiet[(fcn[file_String, symbols] := OpenAndRead[file, fcn[#, symbols] &];
           SetDelayed @@ Hold[fcn[file_Symbol, symbols], a]), 
           {RuleDelayed::"rhs"}]

在他的回答中解释了改变的原因.myReader如上所述定义并检查其定义,我们得到

myReader[file$_String,a_,b_]:=OpenAndRead[file$,myReader[#1,a_,b_]&]
myReader[file_Symbol,a_,b_]:={file,a,b}


@belisarius:`TagSet`,`UpSet`(`^ =`)和`UpSetDelayed`(`^:=`)为相关函数生成upvalues.由于内置函数是"受保护的",因此很难用任何类型的数学行为创建新对象.这些函数通过将变换与对象本身相关联而不是操作来解决这个问题.我从Quanthe Methods with Mathematica那里学到了它们(http://www.amazon.com/Quantum-Methods-Mathematica-James-Feagin/dp/0387953655/ref=tmm_pap_title_0?ie=UTF8&qid=1289761092&sr=8-2)

2> WReach..:

我迟到了派对,接受了所有答案,但我想指出表格的定义:

f[...] := Module[... /; ...]

在这方面非常有用.这种定义可以在最终拯救并决定该定义毕竟不适用之前执行复杂的计算.

我将说明如何在另一个SO问题的特定情况下使用它来实现各种错误处理策略.问题是搜索固定的对列表:

data = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 15}, {5, 29}, {6, 50}, {7,
     88}, {8, 130}, {9, 157}, {10, 180}, {11, 191}, {12, 196}, {13, 
    199}, {14, 200}};

找到第二个组件大于或等于指定值的第一对.找到该对后,将返回其第一个组件.在Mathematica中有很多方法可以写这个,但这里有一个:

f0[x_] := First @ Cases[data, {t_, p_} /; p >= x :> t, {1}, 1]

f0[100] (* returns 8 *)

现在的问题是,如果使用无法找到的值调用函数会发生什么?

f0[1000]
error: First::first: {} has a length of zero and no first element.

错误信息是神秘的,至多没有提供关于问题的线索.如果在调用链中深入调用此函数,则可能会发生级联的类似不透明错误.

处理这种特殊情况有各种策略.一种是更改返回值,以便可以将成功案例与失败案例区分开来:

f1[x_] := Cases[data, {t_, p_} /; p >= x :> t, {1}, 1]

f1[100] (* returns {8} *)
f1[1000] (* returns {} *)

但是,有一个强大的Mathematica传统,只要使用其域外的参数计算函数,就不会修改原始表达式.这是模块[... /; ...]模式可以帮助:

f2[x_] :=
  Module[{m},
    m = Cases[data, {t_, p_} /; p >= x :> t, {1}, 1];
    First[m] /; m =!= {}
  ]

f2[100] (* returns 8 *)
f2[1000] (* returns f2[1000] *)

请注意,如果最终结果是空列表并且原始表达式返回未评估,则f2完全退出 - 通过添加/的简单权宜之计实现; 条件到最后的表达.

如果出现"未找到"案例,可能会决定发出有意义的警告:

f2[x_] := Null /; Message[f2::err, x] 
f2::err = "Could not find a value for ``.";

通过此更改,将返回相同的值,但将在"未找到"情况下发出警告消息.新定义中的Null返回值可以是任何值 - 它不被使用.

有人可能会进一步决定除了有缺陷的客户端代码之外,根本不会发生"未找到"的情况.在这种情况下,应该导致计算中止:

f2[x_] := (Message[f2::err, x]; Abort[])

总之,这些模式很容易应用,因此可以处理定义域之外的函数参数.在定义函数时,花一些时间来决定如何处理域错误是值得的.它减少了调试时间.毕竟,几乎所有功能都是Mathematica中的部分功能.考虑一下:可以使用字符串,图像,歌曲或漫游的纳米机器人来调用函数(可能在Mathematica 9中).

最后的注意事项......我应该指出,在使用多个定义定义和重新定义函数时,由于"遗留"定义,很容易得到意想不到的结果.作为一般原则,我强烈建议使用Clear前面的多重定义函数:

Clear[f]
f[x_] := ...
f[x_] := Module[... /; ...]
f[x_] := ... /; ...

推荐阅读
凹凸曼00威威_694
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有