% Special-Operator UNWIND-PROTECT
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. 評価
なし。