Special Operator UNWIND-PROTECT
unwind-protect
protected-form cleanup-form* => result*
protected-form - フォーム
cleanup-form - フォーム
result - protected-formの返却値
unwind-protect
は、 protected-formを評価し、 unwind-protect
を退出したあとで それが通常の終了か、何かの種類の制御遷移による中断かに関わらず、 cleanup-formを実行することを保証します。 unwind-protect
は、protected-form評価後に 確実に特定の副作用を実行することを意図しています。
もし非ローカル退出がcleanup-form実行中にで生じたときは、 何の特殊なアクションも行いません。 unwind-protect
のcleanup-formは、 unwind-protect
によって保護されているわけではありません。
unwind-protect
は、protected-formから 全ての退出の実行に対して保護されます。 退出は、go
、ignore-errors
、 restart-case
、return-from
、 throw
、with-simple-restart
が含まれます。
退出によるhandler
とrestart
の束縛の解除は、 動的変数とcatch
タグの束縛の解除と並行して、 それらの確立とは逆順に行われます。 この効果は、unwind-protect
に入ったときに、 cleanup-formをhandler
とrestart
の束縛や 動的変数の束縛およびcatch
タグと 同じように見ることができるというものです。
tagbody
(let ((x 3))
(unwind-protect
(if (numberp x) (go out))
(print x)))
(
out ...)
go
が実行されたとき、 最初にprint
が実行され、 そのあとにタグout
の制御の遷移が完了します。
defun dummy-function (x)
(setq state 'running)
(unless (numberp x) (throw 'abort 'not-a-number))
(setq state (1+ x))) => DUMMY-FUNCTION
(catch 'abort (dummy-function 1)) => 2
(=> 2
state catch 'abort (dummy-function 'trash)) => NOT-A-NUMBER
(=> RUNNING
state catch 'abort (unwind-protect (dummy-function 'trash)
(setq state 'aborted))) => NOT-A-NUMBER
(=> ABORTED state
下記のコードは正しくありません。
unwind-protect
(progn (incf *access-count*)
(
(perform-access))decf *access-count*)) (
もしincf
完了前に退出が生じたとき、 decf
フォームはやはり実行されるので、 その結果*access-count*
は正しくない値になります。
let ((old-count *access-count*))
(unwind-protect
(progn (incf *access-count*)
(
(perform-access))setq *access-count* old-count)))
(;;; 下記は2が返却されます。
block nil
(unwind-protect (return 1)
(return 2)))
(
;;; 下記は未定義です。
block a
(block b
(unwind-protect (return-from a 1)
(return-from b 2))))
(
;;; 下記は2が返却されます。
catch nil
(unwind-protect (throw nil 1)
(throw nil 2)))
(
;;;
;;; 下記の例ではBのcatchの上を最初のTHROWによって通過しているため
;;; 結果は未定義であり、したがって移植可能なプログラムは
;;; 動的エクステントが終了していると仮定してください。
;;; catchタグの束縛はまだ解除されていないので、
;;; これは2番目のthrowのターゲットです。
catch 'a
(catch 'b
(unwind-protect (throw 'a 1)
(throw 'b 2))))
(
;;;
;;; 下記の例は "The inner catch returns :SECOND-THROW" が出力され、
;;; :OUTER-CATCHが返却されます。
catch 'foo
(format t "The inner catch returns ~s.~%"
(catch 'foo
(unwind-protect (throw 'foo :first-throw)
(throw 'foo :second-throw))))
(
:outer-catch)
;;; 下記の例は10が返却されます。内側のAのCATCHは素通りされますが
;;; なぜならそのAのCATCHはAへのTHROW実行前に解除されるため
;;; 見ることができません。
catch 'a
(catch 'b
(unwind-protect (1+ (catch 'a (throw 'b 1)))
(throw 'a 10))))
(
;;; 下記の例は未定義です。なぜなら(THROW 'FOO ...)が開始されたとき
;;; (CATCH 'BAR ...)の退出の範囲は終わっているため。
catch 'foo
(catch 'bar
(unwind-protect (throw 'foo 3)
(throw 'bar 4)
(print 'xxx))))
(
;;; 下記の例は4を返却します。XXXは出力されません。
;;; BARのcatchのスコープ上では(THROW 'FOO ...)に効果はありません。
;;; そして(CATCH 'BAR ...)により退出します。
catch 'bar
(catch 'foo
(unwind-protect (throw 'foo 3)
(throw 'bar 4)
(print 'xxx))))
(
;;; 下記の例では5を出力します。
block nil
(let ((x 5))
(declare (special x))
(unwind-protect (return)
(print x)))) (
なし。
なし。
catch
, go
, handler-case
, restart-case
, return
, return-from
, throw
, 3.1. 評価
なし。