「ANSI Common Lisp」5章読了
5章は制御構造のお話し。
普通(?)の言語なら、ここで条件分岐と繰り返しについて語られるところですが、いきなりブロックときたもんだ。
ブロック
ブロックを作るには、progn、block、tagbodyがあるが、
普通はprogn、途中での飛び出し(処理中断)が必要であれば、blockを使う。tagbodyは明示的にはあまり使わないそうな。
progn
* (progn (format t "a") (format t "b") (+ 11 12)) ab 23 *
block
* (block head (format t "Here we go.") (return-from head 'idea) (format t "we'll never see this.")) ; (FORMAT T "we'll never see this.") ; Note: Deleting unreachable code. Here we go.; IDEA *
なんかワーニングが出た。
ブロックとラムダの関係はどうなのだろうか。
ここまでの説明ではまだよくわからない。
コンテクスト
letのようなオペレータは新たなレキシカルコンテキスト(lexical context)を作る。
ときて、良くレキシカルスコープとかいう言葉聞くよなあとか思い出した。
訳注に、
Lispにおいては、「プログラムコードのテキストの上で決まる」というほどの意味。
とあって、やっぱり意味が分からない。。
静的スコープ - Wikipediaを読むと、
なんとなく分かったような。
目的は、
静的スコープはプログラミングの安全性や、最適化が行いやすい
なんですね。
* (let ((x 1)) x) 1 * x ←レキシカルスコープの外なので、参照できない。 Error in KERNEL::UNBOUND-SYMBOL-ERROR-HANDLER: the variable X is unbound. [Condition of type UNBOUND-VARIABLE] Restarts: 0: [ABORT] Return to Top-Level. Debug (type H for help) (EVAL X) Source: Error finding source: Error in function DEBUG::GET-FILE-TOP-LEVEL-FORM: Source file no longer exists: target:code/eval.lisp. 0]
まあ、こういうこと? (そんな単純なわけない)
let*
let*を使うとこんなのが書ける。
* (let* ((x 1) (y (+ x 1))) (+ x y)) 3
普通のletだと、yで使っているxがまだ未定義の認識になってしまうのでエラーになる。
let使って書くならこうですね。
* (let ((x 1)) (let ((y (+ x 1))) (+ x y))) ; 3 *
条件式
ifはthenとelseの両方を持つが、whenはthenの場合のみ実行される仕組みらしい。
* (when (oddp 1) (format t "Hmm, that's odd.") (+ 1 1)) Hmm, that's odd. 2 * (when (oddp 2) (format t "Hmm, that's odd.") (+ 1 1)) NIL *
whenの反対はunless。
condは、switch文みたいな感じ。
* (defun cond-test (a) (cond ((eq a 'a) "A") ((eq a 'b) "B") (t "Z"))) ; COND-TEST * (cond-test 'a) "A" * (cond-test 'b) "B" * (cond-test 'c) "Z" *
反復
新しい要素としてdotimesマクロが出ている。
* (dotimes (x 5 x) (format t "~A " x)) 0 1 2 3 4 5 *
doマクロよりもすっきり書けていい感じです。
0から(n-1)に限定されますが、大抵の場合それで十分なような気がします。
多値
関数の戻り値が複数あることもあるよ。という話らしい。
多値を返す関数の例として、get-decoded-timeがあげられている。
* (get-decoded-time) 52 57 13 12 8 2008 1 NIL -9 *
Nine values are returned: second, minute, hour, date, month, year, day-of-week, daylight-saving-time-p, and time-zone.
だそうです。
daylight-saving-time-pってなんなんでしょうねって思ったら、
サマータイム(夏時間)かどうかのフラグらしい。
日本でも導入検討されているアレのことですね。とりあえずnilになってるけど、今のところ常にnilなのかなあ。
multiple-value-bindで多値の受け取り、multiple-value-callやmultiple-value-listで多値を引数としてほかの関数へ渡すことができる。
割り込み
最近のオブジェクト指向言語にはよくある、例外機構のことらしい。
* (defun super () (catch 'abort (sub) (format t "We'll never see this."))) SUPER * (defun sub () (throw 'abort 99)) SUB * (super) 99 *
これは、正しく割り込みを受け取った場合。
ちなみに受け取れないとエラーになる。
* (defun super () (catch 'abort (sub) (format t "We'll see this."))) SUPER * (defun sub () (throw 'aborts 99)) SUB * (super) Attempt to THROW to a tag that does not exist: ABORTS [Condition of type KERNEL:SIMPLE-CONTROL-ERROR] Restarts: 0: [ABORT] Return to Top-Level. Debug (type H for help) (KERNEL::UNSEEN-THROW-TAG-ERROR-HANDLER "<error finding name>" #.(SYSTEM:INT-SAP #x3FFFCB5C) #<Alien (* #) at #x3FFFC7C4> (142)) Source: Error finding source: Error in function DEBUG::GET-FILE-TOP-LEVEL-FORM: Source file no longer exists: target:code/interr.lisp. 0]