Error handling Mathematica中的可靠清理

Error handling Mathematica中的可靠清理,error-handling,wolfram-mathematica,Error Handling,Wolfram Mathematica,不管是好是坏,Mathematica提供了大量的结构,允许您进行非本地控制传输,包括、/和。然而,这些类型的非本地控制传输通常与编写健壮的程序相冲突,这些程序需要确保清理代码(如关闭流)得到运行。许多语言提供了确保清理代码在各种情况下运行的方法;java有其最后,等等。p> 在数学中,我不知道如何完成同样的事情。我有一个部分解决方案,如下所示: Attributes[CleanUp] = {HoldAll}; CleanUp[body_, form_] := Module[{return,

不管是好是坏,Mathematica提供了大量的结构,允许您进行非本地控制传输,包括、/和。然而,这些类型的非本地控制传输通常与编写健壮的程序相冲突,这些程序需要确保清理代码(如关闭流)得到运行。许多语言提供了确保清理代码在各种情况下运行的方法;java有其<代码>最后 unWror保护< /C>,等等。p> 在数学中,我不知道如何完成同样的事情。我有一个部分解决方案,如下所示:

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
  ]