ITコンサルの日常

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

「Ansi Common Lisp」10章練習問題

1. xがa、yがb、zが(c d)であるとして、これらの変数のみから以下のそれぞれを出力するようなバッククォート式を書け。

(a) ((C D) A Z)
(b) (X B C D)
(c) ((C D A) Z)
; 前提
(setf x 'a)
(setf y 'b)
(setf z '(c d))

; (a)
`(,z ,x z)

; (b)
`(x ,y ,@z)

; (c)
`((,@z ,x) z)

結果はこう。

* Warning:  Declaring X special.

A
* Warning:  Declaring Y special.

B
* Warning:  Declaring Z special.

(C D)
* 
((C D) A Z)
* 
(X B C D)
* 
((C D A) Z)
* 

2. condを用いてifを定義せよ。

(defmacro my-if (cond ex-t ex-nil)
  `(cond (,cond ,ex-t)
         (t ,ex-nil)))

(my-if t 
  (format t "true")
  (format t "false"))

(my-if nil
  (format t "true")
  (format t "false"))

(my-if (> 10 5)
  (format t "true")
  (format t "false"))

(my-if (cdr '(a))
  (format t "true")
  (format t "false"))

こんなんでいいのかい。結果はこう。

* 
MY-IF
* true
NIL
* false
NIL
* true
NIL
* false
NIL
* 

3. 数nとそれに続く1個以上の式を引数とし、n番目の式の値を返すマクロを定義せよ。

> (let ((n 2))
(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))
3

(defmacro nth-expr (n &rest args)
  `(eval (nth (- ,n 1) (quote ,args))))

(let ((n 2))
  (nth-expr n (+ 1 1) (+ 1 2) (+ 1 3)))

なんかごり押し。。
結果はこう。

* 
NTH-EXPR
* 
3
* 

4. ntimes(149ページ)をdoではなく(局所)再帰関数に展開するように定義せよ。

(defmacro ntimes (n &rest body)
  `(labels ((do-ntimes (n)
              (if (< n 0)
                nil
                (progn ,@body
                       (do-ntimes (- n 1))))))
   (do-ntimes ,n)))

(ntimes 10
  (princ "."))

結果はこう。

* 
NTIMES
* ...........
NIL
* 

5. 数nと1つの式を引数として、その式が返すn個の連続する値をリストとして返す、n-ofというマクロを定義せよ。

> (let ((i 0) (n 4))
(n-of n (incf i)))
(1 2 3 4)

(defmacro n-of (n expr)
  (let ((x (gensym)))
    `(let ((result nil))
       (dotimes (,x ,n ,x)
         (setf result (append result (list ,expr))))
       result)))

(let ((i 0) (n 4))
  (n-of n (incf i)))

結果はこう。

* 
N-OF
* 
(1 2 3 4)
* 

再帰で書いた方が副作用もなくていいのは分かってるんですが、どうも反復の方が書きやすい気がしてしまいますね。。

6. 変数のリストとコード本体を引数として、コード本体が評価された後で変数がもとの値に戻るようなマクロを定義せよ。

うーん、どうも分からないので答え参照。
http://www.shido.info/lisp/pacl2.html#macro

(defmacro retain (parms &body body)
  `((lambda ,parms ,@body) ,@parms))

body をクロージャーで隔離することによって 外側の変数は変化しない。

おー、クロージャーって保護の目的でも使えるわけですね。

7. 以下のpushの定義ではどこが誤っているか。本当のpushであればしないような動作を示す呼び出しの例を挙げよ。

(defmacro push (obj lst)
`(setf ,lst (cons ,obj ,lst)))

,lstが二回現れているから、これが評価されるたびに変わるようだとだめなんでしょうね。
具体例は、、うーん。。

8. 引数を倍にするマクロを定義せよ。

> (let ((x 1))
(double x)
x)
2

(defmacro double (x)
  (let ((g (gensym)))
    `(let ((,g ,x))
	   (setf ,g (* ,g 2))
	   (setf ,x ,g))))

(let ((x 1))
  (double x)
  x)

結果はこう。

* 
DOUBLE
* 
2
*