gpt4 book ai didi

error-handling - Mathematica 中的可靠清理

转载 作者:行者123 更新时间:2023-12-03 23:53:52 24 4
gpt4 key购买 nike

无论好坏,Mathematica 提供了丰富的结构,允许您进行非本地控制转移,包括 Return , Catch / Throw , Abort Goto .然而,这些类型的非本地控制转移通常与编写需要确保清理代码(如关闭流)运行的健壮程序相冲突。许多语言提供了确保清理代码在各种情况下运行的方法; 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[##]) &]];

这当然不会赢得任何选美比赛,但它也只能处理 AbortThrow .特别是在 Return 存在的情况下它会失败;我想你是否在使用 Goto在 Mathematica 中进行这种非本地控制,您应得的。

我看不出有什么好的办法来解决这个问题。没有 CheckReturn例如,当你开始做这件事时, Return有相当模糊的语义。我缺少什么技巧吗?

编辑: 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”而不打印清理。我看不出没有繁琐、容易出错且可能不可能的代码遍历或以某种方式在本地重新定义的方法 Return使用 Block ,这是令人发指的hacky并且实际上似乎不起作用(尽管尝试使用它是完全楔入内核的好方法!)

最佳答案

好问题,但我不同意 Return 的语义浑浊;它们记录在您提供的链接中。总之,Return退出调用它的最内层结构(即控制结构或函数定义)。

您的 CleanUp 的唯一情况上面的函数无法从 Return 中清除是当你直接传单或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"
Return退出 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 处被评估(因为 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"

正如我上面提到的,后两个例子是我能想到的唯一有问题的情况(虽然我可能是错的)但可以通过向 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"

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

回复您的更新

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

如果您有更复杂的 If声明,你最好使用 ThrowCatch打破评估并将某些东西“归还”到你希望它被归还的地方。

也就是说,我意识到您可能无法始终控制您必须在之后清理的代码,因此您始终可以将表达式包装在 CleanUp 中。在无操作控制结构中,例如:
ret1 = Do[ret2 = expr, {1}]

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

哼!

关于error-handling - Mathematica 中的可靠清理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3365794/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com