Next: Examples of Catch, Previous: Nonlocal Exits, Up: Nonlocal Exits
catch
とthrow
ほとんどの制御構造は、その構造内での制御の流れだけに影響します。
関数throw
は、通常のプログラム実行のこのような規則の例外です。
つまり、要求に従って非ローカルな脱出を行います。
(ほかにも例外はあるが、それらはエラー処理のためだけである。)
throw
はcatch
の内側で使い、
そのcatch
へ戻ります。
(defun foo-outer () (catch 'foo (foo-inner))) (defun foo-inner () ... (if x (throw 'foo t)) ...)
フォームthrow
を実行すると、対応するcatch
へ制御が戻り、
そのcatch
はただちに終了します。
throw
に続くコードは実行されません。
throw
の第2引数は、catch
の戻り値として使われます。
関数throw
は、その第1引数に基づいて対応するcatch
を探します。
つまり、catch
の第1引数が
throw
に指定されたものにeq
であるcatch
を探します。
そのようなcatch
が複数個ある場合には、
もっとも内側のものを優先します。
したがって、上の例では、throw
はfoo
を指定し、
foo-outer
のcatch
は同じシンボルを指定しているので、
そのcatch
を使います
(ただし、これらのあいだには他の一致するcatch
がないとして)。
throw
の実行により、
対応するcatch
までのすべてのLispの構造を抜け出します。
これには関数呼び出しも含みます。
let
や関数呼び出しなどの束縛を作る構造からもこのように抜け出すので、
通常どおり抜け出す場合と同様に束縛を解きます
(see Local Variables)。
同様に、throw
は、save-excursion
(see Excursions)で
保存したバッファや位置情報、
save-restriction
で保存したナロイング状態、
save-window-excursion
(see Window Configurations)で保存した
ウィンドウの選択状態も復元します。
さらに、スペシャルフォームunwind-protect
で設定した後始末を
このフォームから抜け出すときに実行します(see Cleanups)。
throw
は、テキスト上で、
ジャンプ先であるcatch
の内側に現れる必要はありません。
throw
は、catch
内から呼ばれた別の関数からも戻ることもできます。
throw
の実行が、
時間的にcatch
に入ったあとで、かつ、それから抜けるまえである限り、
throw
は対応するcatch
を参照できます。
エディタコマンドループ(see Recursive Editing)から抜ける
exit-recursive-edit
などのコマンドで
throw
を使えるのは、このような理由からです。
Common Lispに関した注意: Common Lispを含むほとんどの他のLispには、 非逐次的に制御を移す方法がいくつかある。 たとえば、return
、return-from
、go
。 Emacs Lispにはthrow
しかない。
catch
は、関数throw
向けに戻り位置を確立する。 その戻り位置は、tagによって他の戻り位置と区別される。 tagは、nil
以外ならば任意のLispオブジェクトでよい。 引数tagは、戻り位置を確立するまえに、通常どおり評価される。戻り位置を確立してから、
catch
は、bodyのフォームを テキスト上の順に評価する。 エラーや非ローカル脱出なしにフォームの実行が普通に終了した場合、catch
は、最後の本体フォームの値を返す。bodyの内側で、tagと同じ値を指定した
throw
が実行されると、catch
はただちに終了する。 このとき返す値は、throw
の第2引数に指定されたものである。