Special-Operator UNWIND-PROTECT

UP


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-protectcleanup-formは、 unwind-protectによって保護されているわけではありません。

unwind-protectは、protected-formから 全ての退出の実行に対して保護されます。 退出は、goignore-errorsrestart-casereturn-fromthrowwith-simple-restartが含まれます。

退出によるhandlerrestartの束縛の解除は、 動的変数とcatchタグの束縛の解除と並行して、 それらの確立とは逆順に行われます。 この効果は、unwind-protectに入ったときに、 cleanup-formhandlerrestartの束縛や 動的変数の束縛および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. 評価

備考

なし。


TOP, Github