К лучшему или худшему, Mathematica предоставляет множество конструкций, которые позволяют выполнять нелокальные передачи управления, в том числе Return
, Catch
/Throw
, Abort
и Goto
. Однако эти виды нелокальных передач контроля часто конфликтуют с написанием надежных программ, которые должны гарантировать, что код очистки (например, закрывающие потоки) будет запущен. Многие языки предоставляют способы обеспечения того, чтобы код очистки запускался в самых разных обстоятельствах; Java имеет свои блоки finally
, у С++ есть деструкторы, 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[##]) &]];
Это, безусловно, не выиграет никаких конкурсов красоты, но также обрабатывает только Abort
и Throw
. В частности, он терпит неудачу в присутствии Return
; Я полагаю, что если вы используете Goto
для выполнения такого нелокального управления в Mathematica, вы заслуживаете того, что получаете.
Я не вижу в этом хорошего пути. Например, нет CheckReturn
, и когда вы дойдете до него, Return
имеет довольно мутную семантику. Есть трюк, который мне не хватает?
EDIT: Проблема с 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
, который ужасно взломан и на самом деле не работает (хотя экспериментирует с ним это отличный способ полностью вклинивать ядро!)