防止 Mathematica 中出现雪崩的运行时错误
当笔记本超出几个功能时,我遇到的典型情况是——我计算一个表达式,但我得到的不是正确答案,而是Beep,后面是几十个无用的警告,后面是“进一步输出..” . 将被抑制”
我发现有用的一件事——在函数内部使用类似 Python 的“断言”来强制内部一致性。还有其他提示吗?
Assert[expr_, msg_] := If[Not[expr], Print[msg]; Abort[], None]
编辑 11/14 警告雪崩的一般原因是子表达式的计算结果为“坏”值。这会导致父表达式计算为“坏”值,并且这种“坏”会一直传播到根。一路评估的内置程序会注意到不良情况并产生警告。 “坏”可能意味着一个具有错误 Head 的表达式、具有错误数量的元素的列表、负定矩阵而不是正定矩阵等。通常,它不符合父表达式的语义。
处理这个问题的一种方法是重新定义所有函数,以在“错误输入”时返回未计算的值。这将处理内置函数生成的大多数消息。执行“Part”等结构操作的内置函数仍会尝试评估您的值并可能会产生警告。
将调试器设置为“消息中断”可以防止大量错误,尽管始终打开它似乎有些过分
A typical situation I run into when notebook grows beyond a couple of functions -- I evaluate an expression, but instead of correct answer I get Beep followed by dozens of useless warnings followed by "further Output of ... will be suppressed"
One thing I found useful -- use Python-like "assert" inside functions to enforce internal consistency. Any other tips?
Assert[expr_, msg_] := If[Not[expr], Print[msg]; Abort[], None]
edit 11/14
A general cause of a warning avalanche is when a subexpression evaluates to "bad" value. This causes the parent expression to evaluate to a "bad" value and this "badness" propagates all the way to the root. Built-ins evaluated along the way notice the badness and produce warnings. "Bad" could mean an expression with wrong Head, list with wrong number of elements, negative definite matrix instead of positive definite, etc. Generally it's something that doesn't fit in with the semantics of the parent expression.
One way do deal with this is to redefine all your functions to return unevaluated on "bad input." This will take care of most messages produced by built-ins. Built-ins that do structural operations like "Part" will still attempt to evaluate your value and may produce warnings.
Having the debugger set to "break on Messages" prevents an avalanche of errors, although it seems like an overkill to have it turned on all the time
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
正如其他人指出的那样,可以通过三种方法以一致的方式处理错误:
正如 Samsdram 指出的那样,正确输入函数将有助于很重要。不要忘记
模式
,因为有时以这种形式表达某些模式更容易,例如x:{{_, _} ..}
。显然,当这还不够时PatternTest
(?
) 和条件
(/;
) 是可行的方法。 Samdram 很好地涵盖了这一点,但我想补充一点,您可以通过纯函数创建自己的模式测试,例如f[x_?(Head[#]===List&)]
是相当于f[x_List]
。请注意,使用纯函数的&符号形式时,括号是必需的。处理生成的错误的最简单方法显然是
Off
,或者更本地化安静
。在大多数情况下,我们都同意完全关闭我们不想要的消息是一个坏主意,但是当您知道自己正在做一些会生成的消息时,Quiet
会非常有用抱怨,但在其他方面是正确的。Throw
和Catch
有它们的位置,但我觉得它们应该只在内部使用,并且您的代码应该通过消息
设施。可以按照与设置使用消息相同的方式创建消息。我相信可以使用函数检查
,CheckAbort
,AbortProtect
。示例
我的代码中的一个示例是
OpenAndRead
,它可以防止在中止读取操作时留下打开的流,如下所示:直到最近,它还有使用
但是,每次这样做都很烦人。
这就是 belisarius 解决方案发挥作用的地方,通过创建一个您可以持续使用的方法。不幸的是,他的解决方案有一个致命的缺陷:你失去了对语法突出显示功能的支持。因此,这是我想出的一个替代方案,用于从上面挂钩到
OpenAndRead
,它已经使用了。
现在,检查
myReader
的定义给出了两个定义,就像我们想要的那样。但在函数体中,file
必须引用为file$
。 (我还没有弄清楚如何按照我的意愿命名文件 var。)编辑:
MakeCheckedReader
本身并不实际执行任何操作。相反,TagSet
(/:
) 规范告诉 Mathematica,当在SetDelayed
的 LHS 上找到MakeCheckedReader
时,将其替换为所需的函数定义。另外,请注意Quiet
的使用;否则,它会抱怨等式右侧出现的模式a_
和b_
。编辑2:Leonid指出如何使用
文件<定义检查的阅读器时, /code> 不是
file$
。更新后的解决方案如下:更改的原因在此 他的回答。如上所述定义
myReader
并检查其定义,我们得到As others have pointed out, there are three ways to deal with errors in a consistent manner:
As Samsdram pointed out, correctly typing your functions will help a great deal. Don't forget about the
:
form ofPattern
as it is sometimes easier to express some patterns in this form, e.g.x:{{_, _} ..}
. Obviously, when that isn't sufficientPatternTest
s (?
) andCondition
s (/;
) are the way to go. Samdram covers that pretty well, but I'd like to add that you can create your own pattern test via pure functions, e.g.f[x_?(Head[#]===List&)]
is equivalent tof[x_List]
. Note, the parentheses are necessary when using the ampersand form of pure functions.The simplest way to deal with errors generated is obviously
Off
, or more locallyQuiet
. For the most part, we can all agree that it is a bad idea to completely shut off the messages we don't want, butQuiet
can be extremely useful when you know you are doing something that will generate complaints, but is otherwise correct.Throw
andCatch
have their place, but I feel they should only be used internally, and your code should communicate errors via theMessage
facilities. Messages can be created in the same manner as setting up a usage message. I believe the key to a coherent error strategy can be built using the functionsCheck
,CheckAbort
,AbortProtect
.Example
An example from my code is
OpenAndRead
which protects against leaving open streams when aborting a read operation, as follows:which, Until recently, has the usage
However, this is annoying to do every time.
This is where belisarius solution comes into play, by creating a method that you can use consistently. Unfortunately, his solution has a fatal flaw: you lose support of the syntax highlighting facilities. So, here's an alternative that I came up with for hooking into
OpenAndRead
from abovewhich has usage
Now, checking the definition of
myReader
gives two definitions, like we want. In the function body, though,file
must be referred to asfile$
. (I have not yet figured out how to name the file var as I'd wish.)Edit:
MakeCheckedReader
works by not actually doing anything itself. Instead, theTagSet
(/:
) specification tells Mathematica that whenMakeCheckedReader
is found on the LHS of aSetDelayed
then replace it with the desired function definitions. Also, note the use ofQuiet
; otherwise, it would complain about the patternsa_
andb_
appearing on the right side of the equation.Edit 2: Leonid pointed out how to be able to use
file
notfile$
when defining a checked reader. The updated solution is as follows:The reasoning for the change is explained in this answer of his. Defining
myReader
, as above, and checking its definition, we get我来晚了,带着一个被接受的答案和所有的东西,但我想指出形式的定义:
在这种情况下非常有用。这种定义可以执行复杂的计算,然后最终退出并决定该定义毕竟不适用。
我将说明如何在特定情况下使用它来实施各种错误处理策略 另一个SO问题。问题是搜索固定的对列表:
找到第二个分量大于或等于指定值的第一对。一旦找到该对,就将返回其第一个组件。在 Mathematica 中有很多方法可以写这个,但这里只有一种:
现在的问题是,如果使用找不到的值调用该函数会发生什么?
该错误消息充其量是神秘的,没有提供任何有关问题所在的线索。如果在调用链的深处调用此函数,则可能会发生一系列类似的不透明错误。
有多种策略可以处理此类特殊情况。一种是更改返回值,以便区分成功案例和失败案例:
然而,Mathematica 有一个强大的传统,每当使用其域之外的参数对函数进行求值时,都会保留原始表达式不变。这是模块 [... /; 的位置...] 模式可以提供帮助:
请注意,如果最终结果是空列表并且原始表达式未计算返回,则 f2 会完全退出 - 通过添加 / 的简单权宜之计即可实现;最终表达式的条件。
如果发生“未找到”情况,人们可能会决定发出有意义的警告:
通过此更改,将返回相同的值,但在“未找到”情况下将发出警告消息。新定义中的Null返回值可以是任何值——它不被使用。
人们可能会进一步认为“未找到”的情况根本不会发生,除非客户端代码有错误。在这种情况下,应该导致计算中止:
总之,这些模式很容易应用,因此可以处理定义域之外的函数参数。定义函数时,需要花一些时间来决定如何处理域错误。它的好处是减少了调试时间。毕竟,几乎所有函数在 Mathematica 中都是偏函数。考虑一下:可以用字符串、图像、歌曲或流动的纳米机器人群(也许在 Mathematica 9 中)来调用函数。
最后的警告...我应该指出,当使用多个定义来定义和重新定义函数时,由于“剩余”定义,很容易得到意外的结果。作为一般原则,我强烈建议在多重定义函数之前使用 Clear:
I'm coming late to the party, with an accepted answer and all, but I want to point out that definitions of the form:
are very useful in this context. Definitions of this kind can perform complex calculations before finally bailing out and deciding that the definition was not applicable after all.
I will illustrate how this can be used to implement various error-handling strategies in the context of a specific case from another SO question. The problem is to search a fixed list of pairs:
to find the first pair whose second component is greater than or equal to a specified value. Once that pair is found, its first component is to be returned. There are lots of ways to write this in Mathematica, but here is one:
The question, now, is what happens if the function is called with a value that cannot be found?
The error message is cryptic, at best, offering no clues as to what the problem is. If this function was called deep in a call chain, then a cascade of similarly opaque errors is likely to occur.
There are various strategies to deal with such exceptional cases. One is to change the return value so that a success case can be distinguished from a failure case:
However, there is a strong Mathematica tradition to leave the original expression unmodified whenever a function is evaluated with arguments outside of its domain. This is where the Module[... /; ...] pattern can help out:
Note that the f2 bails out completely if the final result is the empty list and the original expression is returned unevaluated -- achieved by the simple expedient of adding a /; condition to the final expression.
One might decide to issue a meaningful warning if the "not found" case occurs:
With this change the same values will be returned, but a warning message will be issued in the "not found" case. The Null return value in the new definition can be anything -- it is not used.
One might further decide that the "not found" case just cannot occur at all except in the case of buggy client code. In that case, one should cause the computation to abort:
In conclusion, these patterns are easy enough to apply so that one can deal with function arguments that are outside the defined domain. When defining functions, it pays to take a few moments to decide how to handle domain errors. It pays in reduced debugging time. After all, virtually all functions are partial functions in Mathematica. Consider: a function might be called with a string, an image, a song or roving swarms of nanobots (in Mathematica 9, maybe).
A final cautionary note... I should point out that when defining and redefining functions using multiple definitions, it is very easy to get unexpected results due to "left over" definitions. As a general principle, I highly recommend preceding multiply-defined functions with Clear:
这里的问题本质上是其中一种类型。一个函数会产生错误的输出(类型不正确),然后将其输入到许多后续函数中,从而产生大量错误。虽然 Mathematica 不像其他语言那样具有用户定义的类型,但您可以对函数参数进行模式匹配,而无需做太多工作。如果匹配失败,该函数不会计算,因此不会发出错误提示音。语法的关键部分是“/;”它位于某些代码的末尾,然后是测试。一些示例代码(输出如下)。
如果测试更简单,还有另一个符号可以进行类似的模式测试“?”并紧跟在模式/函数声明中的参数之后。另一个例子如下。
The problem here is essentially one of types. One function produces a bad output (incorrect type) which is then fed into many subsequent functions producing lots of errors. While Mathematica doesn't have user defined types like in other languages, you can do pattern matching on function arguments without too much work. If the match fails the function doesn't evaluate and thus doesn't beep with errors. The key piece of syntax is "/;" which goes at the end of some code and is followed by the test. Some example code (and output is below).
If the test is simpler, there is another symbol that does similar pattern testing "?" and goes right after an argument in a pattern/function declaration. Another example is below.
它可以帮助定义一个包罗万象的定义来获取错误条件并以有意义的方式报告它:
因此您的顶级调用可以使用 Catch[],或者您可以让它冒泡:
It can help to define a catchall definition to pick up error conditions and report it in a meaningful way:
So your top level calls can use Catch[], or you can just let it bubble up:
我希望得到的是一种定义通用过程来捕获错误传播的方法,而不需要从根本上改变我现在编写函数的方式,最好不需要添加大量的输入。
这是一个尝试:
:c-: 当然是 Esc c- Esc,一个未使用的符号 (\[CircleMinus]),但任何人都会这样做。
输出:
我们改变的是
这
几乎满足我的前提。
编辑
也许为函数添加不经过错误检查的“裸”定义也很方便。我们可以将 funcDef 规则更改为:
获取
此输出
What I'd love to get is a way to define a general procedure to catch error propagation without the need to change radically the way I write functions right now, preferentially without adding substantial typing.
Here is a try:
The :c-: is of course Esc c- Esc, an unused symbol (\[CircleMinus]), but anyone would do.
Output:
What we changed is
by
This almost satisfies my premises.
Edit
Perhaps it's also convenient to add a "nude" definition for the function, that does not undergo the error checking. We may change the funcDef rule to:
to get for
this output