次: Examples of Catch, 前: Nonlocal Exits, 上: 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引数に指定されたものである。