Error handling Mathematica中的可靠清理
不管是好是坏,Mathematica提供了大量的结构,允许您进行非本地控制传输,包括、/和。然而,这些类型的非本地控制传输通常与编写健壮的程序相冲突,这些程序需要确保清理代码(如关闭流)得到运行。许多语言提供了确保清理代码在各种情况下运行的方法;java有其<代码>最后 unWror保护< /C>,等等。p> 在数学中,我不知道如何完成同样的事情。我有一个部分解决方案,如下所示:Error handling Mathematica中的可靠清理,error-handling,wolfram-mathematica,Error Handling,Wolfram Mathematica,不管是好是坏,Mathematica提供了大量的结构,允许您进行非本地控制传输,包括、/和。然而,这些类型的非本地控制传输通常与编写健壮的程序相冲突,这些程序需要确保清理代码(如关闭流)得到运行。许多语言提供了确保清理代码在各种情况下运行的方法;java有其最后,等等。p> 在数学中,我不知道如何完成同样的事情。我有一个部分解决方案,如下所示: Attributes[CleanUp] = {HoldAll}; CleanUp[body_, form_] := Module[{return,
Attributes[CleanUp] = {HoldAll};
CleanUp[body_, form_] :=
Module[{return, aborted = False},
Catch[
CheckAbort[
return = body,
aborted = True];
form;
If[aborted,
Abort[],
return],
_, (form; Throw[##]) &]];
这当然不会赢得任何选美比赛,但它也只处理中止
和抛出
。特别是,它在出现返回时失败
;我想如果你用Goto
在Mathematica中进行这种非局部控制,你应该得到你应得的
我不认为这是个好办法。例如,没有CheckReturn
,当你开始认真研究时,Return
的语义相当模糊。我错过了什么把戏吗
编辑:返回的问题及其定义的模糊性与它与条件句的相互作用有关(在Mathematica中,条件句不是“控制结构”)。例如,使用我的CleanUp
表单:
CleanUp[
If[2 == 2,
If[3 == 3,
Return["foo"]]];
Print["bar"],
Print["cleanup"]]
这将返回“foo”,而不打印“cleanup”。同样地
CleanUp[
baz /.
{bar :> Return["wongle"],
baz :> Return["bongle"]},
Print["cleanup"]]
将返回“bongle”,而不进行打印清理。我看不到一种方法可以解决这个问题,而不需要繁琐、容易出错、甚至不可能的代码遍历,或者使用
Block
本地重新定义Return
,这是一种骇人听闻的黑客行为,实际上似乎不起作用(尽管尝试它是完全嵌入内核的好方法!)好问题,但是我不同意Return
的语义是模糊的;它们记录在您提供的链接中。简而言之,Return
退出调用它的最内部构造(即控件结构或函数定义)
上述CleanUp
函数无法从返回中进行清理的唯一情况是,直接将单个或复合表达式作为输入传递给它(例如(一;二;三)
)
Return退出函数f
:
In[28]:= f[] := Return["ret"]
In[29]:= CleanUp[f[], Print["cleaned"]]
During evaluation of In[29]:= cleaned
Out[29]= "ret"
返回
退出x
:
In[31]:= x = Return["foo"]
In[32]:= CleanUp[x, Print["cleaned"]]
During evaluation of In[32]:= cleaned
Out[32]= "foo"
Return
退出Do
循环:
In[33]:= g[] := (x = 0; Do[x++; Return["blah"], {10}]; x)
In[34]:= CleanUp[g[], Print["cleaned"]]
During evaluation of In[34]:= cleaned
Out[34]= 1
从CleanUp
的主体在计算body
的点处返回(因为CleanUp
是HoldAll
):
如上所述,后两个示例是我能设计的唯一有问题的案例(尽管我可能是错的),但它们可以通过向清理添加定义来处理:
In[44]:= CleanUp[CompoundExpression[before___, Return[ret_], ___], form_] :=
(before; form; ret)
In[45]:= CleanUp[Return["ret"], Print["cleaned"]]
During evaluation of In[46]:= cleaned
Out[45]= "ret"
In[46]:= CleanUp[(Print["before"]; Return["ret"]; Print["after"]),
Print["cleaned"]]
During evaluation of In[46]:= before
During evaluation of In[46]:= cleaned
Out[46]= "ret"
正如你所说,我不会赢得任何选美比赛,但希望这能帮助你解决问题
对您的更新的响应
我认为在If
中使用Return
是不必要的,甚至滥用Return
,因为If
已经根据第一个参数中的条件状态返回了第二个或第三个参数。虽然我意识到您的示例可能是人为的,If[3==3,Return[“Foo”]]
在功能上与相同,如果[3==3,“foo”]
如果您有一个更复杂的If
语句,最好使用Throw
和Catch
来中断计算,并将某个内容“返回”到您希望返回的位置
也就是说,我意识到您可能并不总是能够控制必须清理的代码,因此您可以始终将表达式包装在无操作控制结构中的CleanUp
,例如:
ret1 = Do[ret2 = expr, {1}]
…通过滥用Do
强制expr
中未包含在控制结构中的Return
退出Do
循环。唯一棘手的部分(我想,没有尝试过)必须处理上面两个不同的返回值:ret1
将包含未包含的返回值
,但ret2
将具有expr
的任何其他计算值。可能有更干净的方法来处理,但我现在看不到
嗯!迈克尔·皮拉特提供了“捕捉”功能返回,但我最终使用它的方式略有不同,Return
强制指定函数的返回值以及Do
之类的控制结构。我将之后清理的表达式转换为本地符号的向下值,如下所示:
Attributes[CleanUp] = {HoldAll};
CleanUp[expr_, form_] :=
Module[{body, value, aborted = False},
body[] := expr;
Catch[
CheckAbort[
value = body[],
aborted = True];
form;
If[aborted,
Abort[],
value],
_, (form; Throw[##]) &]];
清理是一个很好的方法。冒着迂腐的风险,我必须指出一个麻烦的用例:
Catch[CleanUp[Throw[23], Print["cleanup"]]]
这个问题是由于不能显式地为将匹配未标记抛出的Catch指定标记模式
以下版本的清理解决了该问题:
SetAttributes[CleanUp, HoldAll]
CleanUp[expr_, cleanup_] :=
Module[{exprFn, result, abort = False, rethrow = True, seq},
exprFn[] := expr;
result = CheckAbort[
Catch[
Catch[result = exprFn[]; rethrow = False; result],
_,
seq[##]&
],
abort = True
];
cleanup;
If[abort, Abort[]];
If[rethrow, Throw[result /. seq -> Sequence]];
result
]
唉,这段代码在选美比赛中更不可能具有竞争力。此外,如果有人跳入另一个非本地控制流,而这段代码无法处理,我也不会感到惊讶。即使它现在处理所有可能的情况,但在Mathematica X中可能会引入有问题的情况(其中X>7.01)
我担心,在Wolfram明确为此目的引入一种新的控制结构之前,这个问题无法有一个明确的答案。UnwindProtect将是这样一个设施的好名字。这是一项值得努力的工作,它适用于最简单的情况,但不幸的是在存在条件时失败。有关详细信息,请参阅编辑的问题更多细节。我同意在条件中使用Return
不是一种很好的Mathematica风格,但在大多数命令式语言中,这是一种非常常见的习惯用法
SetAttributes[CleanUp, HoldAll]
CleanUp[expr_, cleanup_] :=
Module[{exprFn, result, abort = False, rethrow = True, seq},
exprFn[] := expr;
result = CheckAbort[
Catch[
Catch[result = exprFn[]; rethrow = False; result],
_,
seq[##]&
],
abort = True
];
cleanup;
If[abort, Abort[]];
If[rethrow, Throw[result /. seq -> Sequence]];
result
]