可靠的数学清理 [英] Reliable clean-up in Mathematica

查看:155
本文介绍了可靠的数学清理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于更好或更坏的数学,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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆