The Little Schemer 三章

The Little Schemerを読み始めたので、その中の関数をCommon Lispで書いてみる。
三章。


rember (Remove mEMBER) 。

(defun rember (a lat)
  (cond
   ((null lat) nil)
   ((equalp (car lat) a) (cdr lat))
   (t (cons (car lat)
	    (rember a (cdr lat))))))

(rember 'aho '(I am so much aho tensai))
>> (I am so much tensai)

最後の再帰呼び出しの書き方が気になる。これって評価してるみたいに見えて仕方ない。
評価されるところがどういうところなのか分かってないんだろうな。なんだか腑に落ちない。


次は firsts 。引数の各要素の一個目を集めたリストを作る。

(defun firsts (l)
  (cond
   ((null l) nil)
   ((cons (car (car l)) (firsts (cdr l))))))

(firsts '((dou sei ai) (shi na nai) (you hitoto) (mona mona) (ibo) (kuni mura)
	  (tenshi ni love song wo) (gao gao) (rite) (kita minami)))
>> (dou shi you mona ibo kuni tenshi gao rite kita)

引数がリストでない場合はエラーが出て落ちる。
(atom l)とか(atom (car l))の場合はnil返そうと思ったんだけど、本にはそんなこと書いてないのでやめておいた。


続いては insertR 。(insertR new old lat)で、latリストのoldと一致する要素の右側(R)にnewをつけて返す関数。ただしoldとの一致は一回しか見ない。

(defun insertR (new old lat)
  (cond
   ((null lat) nil)
   ((equalp (car lat) old)
    (cons (car lat)
	  (cons new (cdr lat))))
   (t (cons (car lat)
	    (insertR new old (cdr lat))))))

(insertR 'chocolate 'hot '(I would like to drink some hot or milk))
>> (I would like to drink some hot chocolate or milk)


同様に insertL 。今度は左側に追加するだけ。latを分解しなくても良いぶんだけRよりもかんたん。

(defun insertL (new old lat)
  (cond
   ((null lat) nil)
   ((equalp (car lat) old)
    (cons new lat))
   (t (cons (car lat)
	    (insertL new old (cdr lat))))))

(insertL 'chocolate 'hot '(I would like to drink some hot or milk))
>> (I would like to drink some chocolate hot or milk)

そんな感じ。


続いては subst 。(subst new old lat) で、lat内にはじめて出現するoldをnewに置き換える。
insertLやRよりもかんたんだ。

(defun subst (new old lat)
  (cond
   ((null lat) nil)
   ((equalp (car lat) old)
    (cons new (cdr lat)))
   (t (cons (car lat)
	    (subst new old (cdr lat))))))

(subst 'tensai 'aho '(ore ha aho desu))
>> (ore ha tensai desu)

まあそうだよね。


substのちょい変化版、subst2。(subst2 new o1 o2 lat) で、o1またはo2のうち先に出たほうをnewで置き換える。

(defun subst2 (new o1 o2 lat)
  (cond
   ((null lat) nil)
   ((or
     (equalp (car lat) o1)
     (equalp (car lat) o2))
    (cons new (cdr lat)))
   (t
    (cons (car lat)
	  (subst2 new o1 o2 (cdr lat))))))

(subst2 'tensai 'aho 'vaka '(ore ha vaka de aho de shikata nai))
>> (ore ha tensai de aho de shikata nai)

改行の入れ方に一貫性がないのはちょっと反省点。


続いてmultirember。
remberは先頭のひとつだけしか変更しなかった。multiremberは全部リムーブしちゃいます。

(defun multirember (a lat)
  (cond
   ((null lat) nil)
   ((equalp (car lat) a)
    (multirember a (cdr lat)))
   (t (cons (car lat)
	    (multirember a (cdr lat))))))

(multirember 'ta '(kore ta ga ta ta ta tanu ta ta ki ta koto ta ta ba ta ta))
>> (kore ga tanu ki koto ba)


あとはがーっと書いちゃおう。説明も要らないでしょう。
multiinsertR

(defun multiinsertR (new old lat)
  (cond
   ((null lat) nil)
   ((equalp (car lat) old)
    (cons (car lat)
	  (cons new (multiinsertR new old (cdr lat)))))
   (t (cons (car lat)
	    (multiinsertR new old (cdr lat))))))

(multiinsertR 'me 'do '(do do pi do))
>> (do me do me pi do me)

multiinsertL

(defun multiinsertL (new old lat)
  (cond
   ((null lat) nil)
   ((eq (car lat) old)
    (cons new
	  (cons (car lat)
		(multiinsertL new old (cdr lat)))))
   (t (cons (car lat)
	    (multiinsertL new old (cdr lat))))))

(multiinsertL 'me 'do '(do do pi do))
>> (me do me do pi me do)

multisubst

(defun multisubst (new old lat)
  (cond
   ((null lat) nil)
   ((equalp (car lat) old)
    (cons new
	  (multisubst new old (cdr lat))))
   (t (cons (car lat)
	    (multisubst new old (cdr lat))))))

(multisubst 'tensai 'aho '(aho aho ore aho))
>> (tensai tensai ore tensai)

途中、consをcondと書いていたせいで「carなんて変数は宣言されてねぇよ」って怒られた。きをつける。


気づいたらもうこんな時間か。晩飯食べてない。そろそろ帰ろう。