可靠的数学清理 [英] Reliable clean-up in Mathematica
问题描述
返回
, Catch
/ 投掷
, 中止
和 转到
。然而,这些非本地的控制转移通常与编写强大的程序相冲突,这些程序需要确保清理代码(如关闭流)运行。许多语言提供了确保清理代码在各种情况下运行的方法; Java具有最终块,C ++具有析构函数,Common Lisp具有 UNWIND-PROTECT
等等。 在数学中,我不知道如何完成同样的事情。我有一个部分解决方案如下所示:
属性[CleanUp] = {HoldAll};
清理[body_,form_]:=
模块[{return,aborted = False},
Catch [
CheckAbort [
return = body,
aborted = True];
表单;
如果[aborted,
Abort [],
return],
_,(form; Throw [##])&]];
这肯定不会赢得任何美容大赛,但它也只处理中止
和投掷
。特别是,在返回
的存在下失败;我想,如果你使用 Goto
在数学中做这样的非本地控件,你应该得到你所需要的。
我没有看到很好的方法。例如,没有
CheckReturn
,当你直接得到它时,返回
具有相当暗淡的语义。有没有一个诡计? 编辑: 的问题返回
,其定义中的模糊性与其与条件(与某种程度上不是数学中的控制结构)的相互作用有关。一个例子,使用我的 CleanUp
形式:
CleanUp [
如果[2 == 2,
如果[3 == 3,
返回[foo]]];
打印[bar],
打印[清理]]
这将返回foo而不打印清理。同样地,
CleanUp [
baz /。
{bar:>返回[wongle],
baz:>返回[bongle]},
打印[清理]
将返回bongle而不打印清理。我没有看到这样的方式,没有繁琐,容易出错,也许不可能的代码行走或以某种方式在本地重新定义返回
使用阻止
,这是非常黑客,实际上似乎没有工作(虽然尝试它是一个伟大的方式来完全楔一个内核!)
很好的问题,但我不同意返回
的语义是模糊的;它们记录在您提供的链接中。简而言之,返回
退出调用它的最内层结构(即控件结构或函数定义)。
上面的 CleanUp
函数的唯一情况是无法从 Return
中清除,当您直接传递单个或 CompoundExpression
(例如(一;二;三)
直接作为输入。
返回退出函数 f
:
[28]:= f []:=返回[ret]
在[29]中:= CleanUp [f [],打印[清理]]
在评估In [29]时:= clean
Out [29] =ret
返回
退出 x
:
[pre]
在[31]中:= x =返回[foo]
在[32]中:= CleanUp [x,打印[清理] ]
在评估In [32]时:= clean
输出[32] =foo
返回
退出 Do
loop:
在[33]中:= g []:=(x = 0;做[X ++;返回[blah],{10}]; x)
在[34]中:= CleanUp [g [],打印[清理]]
在评估In [34]时:= clean
Out [34] = 1
从正文
被评估的地方,因为 CleanUp
是 HoldAll
):
在[35]中:= CleanUp [Return [ret] ,打印[清理]];
Out [35] =ret
在[36]中:= CleanUp [(打印[之前];返回[ret];打印[之后]),
打印[清理]]
在评估In [36]期间:=
Out [36] =ret
如上所述,后两个例子是我可以设计的唯一有问题的例子(尽管我可以错误),但可以通过向 CleanUp
添加定义来处理它们:
(before; form; ret)
在[45] = CleanUp [返回[ret],打印[清理]]
在评估In [46]时:=清理
输出[45] =ret
在[46]中:= CleanUp [(打印[之前];返回[ret];打印[之后]),
打印[清理 ]
在评估In [46]时= =
在评估In [46]期间:=清理
输出[46] = ret
正如你所说,不会赢得任何人
对您的更新的回应
我会认为在内使用
是不必要的,甚至滥用返回
如果假设
在功能上与如果
已经根据第一个参数中条件的状态返回第二个或第三个参数。虽然我知道你的例子可能是设计的,但如果[3 == 3,Return [Foo]] 相同如果[3 = = 3,foo]
如果你有一个更复杂的如果
你最好使用投掷
和 Catch
来打破评估,并返回一些点你希望它返回到。
那就是说,我意识到你可能不总是控制你必须清理的代码,所以你可以总是包装表达式在 CleanUp
中的无操作控件结构中,例如:
ret1 = Do [ret2 = expr,{1}]
...滥用 do
强制返回
不包含在 expr
中的控件结构中退出 Do
循环。唯一棘手的部分(我认为没有尝试过)是不得不处理上面两个不同的返回值: ret1
将包含一个未包含的$ code的值>返回,但 ret2
将具有 expr
的任何其他评估的值。可能有一个更清洁的方法来处理,但我现在看不到。
HTH!
For better or worse, Mathematica provides a wealth of constructs that allow you to do non-local transfers of control, including Return
, Catch
/Throw
, Abort
and Goto
. However, these kinds of non-local transfers of control often conflict with writing robust programs that need to ensure that clean-up code (like closing streams) gets run. Many languages provide ways of ensuring that clean-up code gets run in a wide variety of circumstances; Java has its finally
blocks, C++ has destructors, Common Lisp has UNWIND-PROTECT
, and so on.
In Mathematica, I don't know how to accomplish the same thing. I have a partial solution that looks like this:
Attributes[CleanUp] = {HoldAll};
CleanUp[body_, form_] :=
Module[{return, aborted = False},
Catch[
CheckAbort[
return = body,
aborted = True];
form;
If[aborted,
Abort[],
return],
_, (form; Throw[##]) &]];
This certainly isn't going to win any beauty contests, but it also only handles Abort
and Throw
. In particular, it fails in the presence of Return
; I figure if you're using Goto
to do this kind of non-local control in Mathematica you deserve what you get.
I don't see a good way around this. There's no CheckReturn
for instance, and when you get right down to it, Return
has pretty murky semantics. Is there a trick I'm missing?
EDIT: The problem with Return
, and the vagueness in its definition, has to do with its interaction with conditionals (which somehow aren't "control structures" in Mathematica). An example, using my CleanUp
form:
CleanUp[
If[2 == 2,
If[3 == 3,
Return["foo"]]];
Print["bar"],
Print["cleanup"]]
This will return "foo" without printing "cleanup". Likewise,
CleanUp[
baz /.
{bar :> Return["wongle"],
baz :> Return["bongle"]},
Print["cleanup"]]
will return "bongle" without printing cleanup. I don't see a way around this without tedious, error-prone and maybe impossible code-walking or somehow locally redefining Return
using Block
, which is heinously hacky and doesn't actually seem to work (though experimenting with it is a great way to totally wedge a kernel!)
Great question, but I don't agree that the semantics of Return
are murky; They are documented in the link you provide. In short, Return
exits the innermost construct (namely, a control structure or function definition) in which it is invoked.
The only case in which your CleanUp
function above fails to cleanup from a Return
is when you directly pass a single or CompoundExpression
(e.g. (one;two;three)
directly as input to it.
Return exits the function f
:
In[28]:= f[] := Return["ret"]
In[29]:= CleanUp[f[], Print["cleaned"]]
During evaluation of In[29]:= cleaned
Out[29]= "ret"
Return
exits x
:
In[31]:= x = Return["foo"]
In[32]:= CleanUp[x, Print["cleaned"]]
During evaluation of In[32]:= cleaned
Out[32]= "foo"
Return
exits the Do
loop:
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
Returns from the body of CleanUp
at the point where body
is evaluated (since CleanUp
is HoldAll
):
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"
As I noted above, the latter two examples are the only problematic cases I can contrive (although I could be wrong) but they can be handled by adding a definition to 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"
As you said, not going to win any beauty contests, but hopefully this helps solve your problem!
Response to your update
I would argue that using Return
inside If
is unnecessary, and even an abuse of Return
, given that If
already returns either the second or third argument based on the state of the condition in the first argument. While I realize your example is probably contrived, If[3==3, Return["Foo"]]
is functionally identical to If[3==3, "foo"]
If you have a more complicated If
statement, you're better off using Throw
and Catch
to break out of the evaluation and "return" something to the point you want it to be returned to.
That said, I realize you might not always have control over the code you have to clean up after, so you could always wrap the expression in CleanUp
in a no-op control structure, such as:
ret1 = Do[ret2 = expr, {1}]
... by abusing Do
to force a Return
not contained within a control structure in expr
to return out of the Do
loop. The only tricky part (I think, not having tried this) is having to deal with two different return values above: ret1
will contain the value of an uncontained Return
, but ret2
would have the value of any other evaluation of expr
. There's probably a cleaner way to handle that, but I can't see it right now.
HTH!
这篇关于可靠的数学清理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!