ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

「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
* 

25.4.1. Time Functionsによると、

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]