代码之家  ›  专栏  ›  技术社区  ›  Pillsy

Mathematica的可靠清理

  •  13
  • Pillsy  · 技术社区  · 15 年前

    Return , Catch / Throw Abort Goto . 然而,这种非本地的控制传输常常与编写健壮的程序冲突,这些程序需要确保清除代码(如关闭的流)得到运行。许多语言提供了确保清理代码在各种各样的环境中运行的方法;Java有自己的特点 finally 块,C++有析构函数,普通Lisp有 UNWIND-PROTECT ,等等。

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

    这当然不会赢得任何选美比赛,但它也只能处理 中止 . 特别是,当有人在场时,它就失败了 ; 我想如果你用的是 在Mathematica中做这种非局部控制,你应该得到你应得的。

    CheckReturn 举个例子,当你认真考虑的时候, 返回 有相当模糊的语义。有什么把戏我错过了吗?

    编辑: 返回 ,以及它定义的模糊性,与它与条件句的相互作用有关(在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"]]
    

    返回 Block ,这是令人发指的黑客行为,实际上似乎不起作用(尽管尝试它是一个伟大的方式,完全楔入一个内核!)

    3 回复  |  直到 15 年前
        1
  •  3
  •   Michael Pilat    15 年前

    好问题,但我不同意 Return 返回 退出调用它的最内部构造(即控制结构或函数定义)。

    CleanUp 上面的函数无法从 返回 当你直接通过一个或多个 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"
    

    出口 x :

    In[31]:= x = Return["foo"]
    
    In[32]:= CleanUp[x, Print["cleaned"]]
    
    During evaluation of In[32]:= cleaned
    
    Out[32]= "foo"
    

    返回 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
    

    body 已评估(自 清理 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"
    

    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 返回 ,鉴于此 如果 If[3==3, Return["Foo"]] If[3==3, "foo"]

    如果你有更复杂的 如果 声明,你最好用 Throw Catch

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

    ret1 = Do[ret2 = expr, {1}]
    

    ... 滥用职权 返回 不包含在中的控制结构中 expr 回到外面 循环。唯一棘手的部分(我想,没有尝试过这个)是必须处理上面两个不同的返回值: ret1 将包含未包含的 返回 ,但是 ret2 出口

    嗯!

        2
  •  3
  •   Community Mohan Dere    6 年前

    Pillsy's later version 属于 清理 是个好主意。冒着迂腐的风险,我必须指出一个麻烦的用例:

    Catch[CleanUp[Throw[23], Print["cleanup"]]]
    

    这个问题是由于不能显式指定 接住 与未标记的 .

    以下版本的 解决这个问题:

    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
      ]
    

    展开保护

        3
  •  2
  •   Community Mohan Dere    9 年前

    迈克尔·皮拉特提供 the key trick 但我最终用了一种稍微不同的方式,使用了 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[##]) &]];