Mathematica的可靠清理

Pil*_*lsy 13 error-handling wolfram-mathematica

是好还是坏,Mathematica提供了丰富的结构,让你做控制的非本地传输,其中Return,Catch/ Throw,AbortGoto.但是,这些非本地控制转移通常与编写需要确保清理代码(如关闭流)运行的强大程序相冲突.许多语言提供了确保清理代码在各种情况下运行的方法; Java有它的finally块,C++有析构函数,Common Lisp有UNWIND-PROTECT,等等.

在Mathematica,我不知道如何完成同样的事情.我有一个部分解决方案,如下所示:

Attributes[CleanUp] = {HoldAll};
CleanUp[body_, form_] :=
  Module[{return, aborted = False},
   Catch[
    CheckAbort[
     return = body,
     aborted = True];
    form;
    If[aborted,
     Abort[],
     return],
    _, (form; Throw[##]) &]];
Run Code Online (Sandbox Code Playgroud)

这肯定不会赢得任何选美比赛,但它也只能处理AbortThrow.特别是,它存在失败Return; 我想如果你Goto在Mathematica中使用这种非本地控制你应得的.

我没有看到这方面的好方法.CheckReturn例如,没有,当你接受它时,它Return具有非常模糊的语义.有缺点我不知道吗?

编辑:问题Return及其定义中的模糊性与其与条件的相互作用有关(在某种程度上它不是Mathematica中的"控制结构").一个例子,使用我的CleanUp表单:

CleanUp[
 If[2 == 2,
  If[3 == 3,
   Return["foo"]]];
 Print["bar"],

 Print["cleanup"]]
Run Code Online (Sandbox Code Playgroud)

这将返回"foo"而不打印"cleanup".同样,

CleanUp[
 baz /.
  {bar :> Return["wongle"],
   baz :> Return["bongle"]},

 Print["cleanup"]]
Run Code Online (Sandbox Code Playgroud)

将返回"bongle"而不打印清理.我没有看到这种方法,没有繁琐,容易出错,可能不可能的代码行走或以某种方式在本地重新定义Return使用Block,这是非常hacky并且实际上似乎没有工作(虽然尝试它是一个很好的方式来完全楔入内核!)

Mic*_*lat 3

很好的问题,但我不同意 的语义Return是模糊的;它们记录在您提供的链接中。简而言之,Return退出调用它的最内部构造(即控制结构或函数定义)。

CleanUp上面的函数无法从 a 中清理的唯一情况Return是当您直接传递单个 or CompoundExpression(例如(one;two;three)直接作为其输入。

Return 退出函数f

In[28]:= f[] := Return["ret"]

In[29]:= CleanUp[f[], Print["cleaned"]]

During evaluation of In[29]:= cleaned

Out[29]= "ret"
Run Code Online (Sandbox Code Playgroud)

Return退出x

In[31]:= x = Return["foo"]

In[32]:= CleanUp[x, Print["cleaned"]]

During evaluation of In[32]:= cleaned

Out[32]= "foo"
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

从 的 主体返回CleanUp被评估的点body(因为CleanUpHoldAll):

In[35]:= CleanUp[Return["ret"], Print["cleaned"]];

Out[35]= "ret"

In[36]:= CleanUp[(Print["before"]; Return["ret"]; Print["after"]), 
 Print["cleaned"]]

During evaluation of In[36]:= before

Out[36]= "ret"
Run Code Online (Sandbox Code Playgroud)

正如我上面指出的,后两个示例是我可以设计的唯一有问题的情况(尽管我可能是错的),但可以通过添加定义来处理它们CleanUp

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"
Run Code Online (Sandbox Code Playgroud)

正如您所说,不会赢得任何选美比赛,但希望这有助于解决您的问题!

回复您的更新

我认为使用ReturninsideIf是不必要的,甚至是滥用Return,因为If已经根据第一个参数中的条件状态返回了第二个或第三个参数。虽然我意识到你的例子可能是人为的,If[3==3, Return["Foo"]]但功能上与If[3==3, "foo"]

如果你有一个更复杂的If语句,你最好使用ThrowCatch来打破评估并将某些内容“返回”到你想要它返回的点。

也就是说,我意识到您可能并不总是能够控制之后必须清理的代码,因此您始终可以将表达式包装在CleanUp无操作控制结构中,例如:

ret1 = Do[ret2 = expr, {1}]
Run Code Online (Sandbox Code Playgroud)

...通过滥用Do强制Return不包含在控制结构中的expr返回到Do循环之外。唯一棘手的部分(我认为,没有尝试过)是必须处理上面的两个不同的返回值:ret1将包含 uncontained 的值Return,但ret2将具有 的任何其他评估的值expr。可能有一种更干净的方法来处理这个问题,但我现在看不到。

哈!