ANSI Common Lisp 2章「Lispの世界へようこそ」

練習問題があったので,解いてみた.

4 2つの数を引数として,大きい方を返す関数

(defun my-max (lst)
  (if (not (cdr lst)) (car lst)
    (if (> (car lst) (my-max (cdr lst)))
	(car lst)
	(my-max (cdr lst)))))

折角だからリストを受け取るようにした.そしたらすげぇごちゃごちゃした.気持ち悪いよ.しかもリストを渡すわけだから,

(my-max (list 1 3 2 4))
;; maxなら, (max 1 3 2 4) で良い

なんてしなければいけないという手間.任意個の引数をとる方法が,きっとあるはず.

7 引数のリストにリストが含まれているか

(defun haslist (lst)
  (or (listp (car lst)) 
      (if (cdr lst)
	  (haslist (cdr lst)))))

んー,現時点ではこれが限界です.

8 再帰

反復と再帰を使ってa,bを解かなければならないけれど,再帰だけにしておく.

a 正の整数を引数とし,その数のドットを表示
(defun dot (x)
  (if (> x 0)
      (funcall #'(lambda ()
		         (format t ".")
		         (dot (- x 1))))))

のっけからlambdaが出てくるこの本はほんと素敵だと思うんだけど,common lispを学ぶ上では,至極普通なのかな.

b リストにaというシンボルが幾つ含まれているか
(defun counta (lst)
  (if (car lst)
      (if (eql 'a (car lst))
	  (+ 1 (counta (cdr lst)))
	  (counta (cdr lst)))
       0))

最後の0がきもいね!

9 nilでない要素の合計

二通り,間違いのコードがあって,それを正そうという問題.

a

まずは間違い.

(defun summit (lst)
  (remove nil lst)
  (apply #'+ lst))

どの辺が間違いかというと,

  1. removeしたところでlstに変更は加えられない.せめて(setf (remove nil lst))とすべき
  2. "+"じゃリストの長さは数えられないじゃね?

で,こうした.

(defun summit (lst)
  (length (remove nil lst)))
b

まずは間違い.

(defun summit (lst)
  (let ((x (car lst)))
    (if (null x)
        (summit (cdr lst))
        (+ x (summit (cdr lst))))))

どの辺が間違いかというと,

  1. 再帰だけど止まらない
  2. 最後の行,xを足してどうする
  3. となると,xは使わなくなるのでlet不要

で,足りない頭を精一杯使ってこうした.

(defun summit (lst)
  (if (cdr lst)
      (if (null (car lst))
      	  (summit (cdr lst))
          (+ 1 (summit (cdr lst))))
      (if (null (car lst)) 0 1)))

とりあえず動く.そりゃごり押しだもん.


第3章では,リストについて詳しく説明してくれるそうです.