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
state => 2
(catch 'abort (dummy-function 'trash)) => NOT-A-NUMBER
state => RUNNING
(catch 'abort (unwind-protect (dummy-function 'trash)
(setq state 'aborted))) => NOT-A-NUMBER
state => ABORTED下記のコードは正しくありません。
(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. 評価
なし。