Zuverlässige Bereinigung in Mathematica

Mathematica bietet eine Fülle von Konstrukten, mit denen Sie nicht-lokale Kontrollübertragungen durchführen können, einschließlichReturn, Catch/Throw, Abort undGoto. Diese Art der nicht-lokalen Übertragung von Kontrolle widerspricht jedoch häufig dem Schreiben robuster Programme, die sicherstellen müssen, dass Bereinigungscode (wie das Schließen von Streams) ausgeführt wird. Viele Sprachen bieten Möglichkeiten, um sicherzustellen, dass Bereinigungscode unter den unterschiedlichsten Umständen ausgeführt wird. Java hat seinfinally blocks, C ++ hat Destruktoren, Common Lisp hatUNWIND-PROTECT, und so weiter

In Mathematica weiß ich nicht, wie ich dasselbe erreichen soll. Ich habe eine Teillösung, die so aussieht:

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

Dies wird sicherlich keine Schönheitswettbewerbe gewinnen, aber es geht auch nur umAbort undThrow. Insbesondere scheitert es bei Vorhandensein vonReturn; Ich denke, wenn Sie @ verwendGoto Um diese Art von nicht lokaler Kontrolle in Mathematica durchzuführen, haben Sie das verdient, was Sie bekommen.

Ich sehe keinen guten Weg, das zu umgehen. Da ist keinCheckReturn zum Beispiel, und wenn Sie es genau wissen,Return hat ziemlich düstere Semantik. Gibt es einen Trick, den ich vermisse?

BEARBEITEN Das Problem mitReturn und die Unbestimmtheit in seiner Definition haben mit der Interaktion mit Bedingungen zu tun (die in Mathematica irgendwie keine "Kontrollstrukturen" sind). Ein Beispiel mit meinemCleanUp bilden

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

 Print["cleanup"]]

Dies gibt "foo" zurück, ohne "cleanup" zu drucken. Gleichfalls

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

 Print["cleanup"]]

wird "bongle" zurückgeben, ohne die Bereinigung zu drucken. Ich sehe keinen Weg, um dies zu umgehen, ohne mühsames, fehleranfälliges und möglicherweise unmögliches Code-Walking oder eine lokale Neudefinition vonReturn usingBlock, was abscheulich ist und nicht wirklich zu funktionieren scheint (obwohl das Experimentieren damit eine großartige Möglichkeit ist, einen Kernel komplett zu verkeilen!)

Antworten auf die Frage(6)

Ihre Antwort auf die Frage