catch および throw ¶ほとんどの制御構造は、その構造内部の制御フローにのみ影響を与えます。関数 throw
は、通常のプログラム実行におけるこの規則の例外であり、要求に応じて非局所的な脱出を行います。(他にも例外はありますが、それらはエラー処理専用です。)throw は catch 内で使用され、その catch へと戻ります。例えば:
(defun foo-outer ()
(catch 'foo
(foo-inner)))
(defun foo-inner ()
...
(if x
(throw 'foo t))
...)
The throw form, if executed, transfers control
straight back to the corresponding catch, which
returns immediately. The code following the throw
is not executed. The second argument of throw is
used as the return value of the catch.
関数 throw は、第1引数に基づいて一致する catch を探します。具体的には、throw
で指定されたものに対し、第1引数が eq である catch を検索します。該当する catch
が複数ある場合は、最も内側のものが優先されます。したがって、上記の例では、throw が foo を指定し、foo-outer 内の catch も同じ記号を指定しているため、catch
が該当します(その間に他に一致する catch がないと仮定した場合)。
throw を実行すると、関数呼び出しを含む、対応する catch までのすべての Lisp 構文が終了します。let
などの束縛構文や関数呼び出しがこのように終了した場合、これらの構文が正常に終了したときと同様に、束縛が解除されます(ローカル変数 を参照)。同様に、throw は save-excursion
によって保存されたバッファと位置(エクスカーション
を参照)、および save-restriction
によって保存されたナローイング状態を復元します。また、unwind-protect
特殊フォームを終了する際に、そのフォームで設定されたクリーンアップ処理を実行します(非ローカル終了からのクリーンアップ を参照)。
throwは、ジャンプ先となるcatchの字句解析上の範囲内に現れる必要はありません。同様に、catch内で呼び出された別の関数から呼び出されることも可能です。throwがcatchへの進入より時系列的に後、かつそこからの退出より時系列的に前に行われる限り、catchにアクセスできます。このため、throwは、エディタのコマンドループに戻るexit-recursive-editのようなコマンドで使用できます(再帰的編集を参照)。
Common Lispに関する注記: Common Lispを含む他のほとんどのLispバージョンでは、非逐次的に制御を移す方法がいくつか用意されています(例えば
return、return-from、goなど)。Emacs Lispにあるのはthrowのみです。cl-lib ライブラリでは、これらの一部のバージョンが提供されています。Common Lisp Extensions の Blocks and Exits を参照してください。
catch は throw
関数の復帰点を設定します。復帰点は、nil 以外の任意の Lisp オブジェクトとなり得る tag によって、他の同様の復帰点と区別されます。引数 tag
は、復帰点が設定される前に通常通り評価されます。
リターンポイントが有効な状態では、catch は body のフォームをテキスト順に評価します。フォームが正常に実行された場合(エラーや nonlocal exit
が発生しなかった場合)、最後のボディフォームの値が catch から返されます。
body の実行中に同じ値 tag を指定した throw が実行されると、catch
フォームは直ちに終了し、返される値は throw の第2引数として指定されたものになります。
throw の目的は、catch
であらかじめ設定された戻り先から復帰することです。引数 tag
は、既存のさまざまな戻り先の中から選択するために使用され、eq から catch で指定された値である必要があります。複数の戻り先が tag に一致する場合は、最も内側のものが使用されます。
その catch から返す値として、引数 value
が使用されます。
タグ tag で戻り先が指定されていない場合、データ (tag value) とともに no-catch エラーが通知されます。