error という関数がある。ヘルプで見てみる。
出た。「例外」だ。なんとなくしか分かってない。うそです。まったく分かっていない(w
試してみる。たとえば、
(defvar a 0)
(defun test ()
(interactive)
(error "!!!") ; ここで抜けてる
(incf a))
;; test
(test) ; 何回呼んでも
a ; a はゼロのまま
0
とかして、何回 test を呼ぼうが (incf a) は実行されないので、a はいつまでたってもゼロのままになる。
じゃあ error ってのはただ脱出するだけなのか。C言語の break や return なのか。ちょっとそれは違うようだ。
handler-case というのを使えば、(error なんちゃら) として発行したエラーを捕まえてそのときのエラー処理ができるようだ。
そんなことをしてなにが面白いのか。
ココが参考になる。例外の仕組みのない C言語と lisp の比較がおもしろい。
例をやってみる。
; 例 1
(handler-case (/ 10 0))
0で除算しました: /: (10 0) ; エラーを補足できてないので、最終的に xyzzy が受け取った
; 例 2
(handler-case (/ 10 0)
(division-by-zero (c) c)) ; division-by-zero 型のエラーを補足して、そのエラーを返す
#S(division-by-zero operation / operands (10 0)) ; これがエラーの中身
ここで、例 2 の形を詳しく見てみる。
例 2 のエラー処理は 単に c となってて、これは、引数 c で捕まえたエラーを単に返すよっていうエラー処理だ。
ややこしいのは、通常処理と補足するエラーの種類(型)が同じ error という名前になったとき。
; 例 3
(handler-case (error "~D番のエラーが起きました" 2)
(error (c) c)) ; error 型のエラーを補足して、そのエラーを返す
#S(simple-error format-string "~D番のエラーが起きました" format-arguments (2))
これは、
- 通常処理で error 関数を使ってエラーを発行しといて、
- それを error という型で捕まえている
関数名も例外の型の名前も、同じ error という名前だからややこしい。
で、上の例 3 には問題があって、この通常処理の (error "文字列") の書式だと 発行するエラーの種類が simple-error というエラー型に固定されてしまう。
じゃあ、例 2 みたいにもっと細かい種類のエラーを発行したいときはどうするかというと、
(handler-case (error (make-condition 'division-by-zero
(error (c) c))
#S(division-by-zero operation / operands (10 0)) ; 例 2 と同じエラーの型にできた
というふうに、文字列ではなくて make-condition で作ってやる必要がある。
今日はここまでにしとこうか。
そういえば昔、 C言語から C++ に移ろうとしたときに例外処理はうまく理解できなくて、使いこなせなかった。でも人のコードではバリバリ使われてて、そのうち C++ のコードを読むのがしんどくなった。
その後 delphi をやり出したけど、やっぱり例外処理を理解してなくて、ただ定型的に書いていた。
まったく恥ずかしい限り。
経験的にいうと、昔解けなかった問題はその後何度も出題されて、そのつど僕を困らせる。逃げても追いかけてくる。
でも、逃げる以外の方法がある。理解することだ。
今回は逃げずに例外と向き合ってみようと思う。
0 コメント :
コメントを投稿