今日から7章。集合(セット)が出てきた。セットは、同じアトムが2回以上現れないリストのこと。
まずは、引数として与えられたリストが、セットかどうかを判定する関数 set? から。
set?
(define member?
(lambda (a lat)
(cond
((null? lat) #f)
(else (or (eq? (car lat) a) (member? a (cdr lat)))))))
(define set?
(lambda (lat)
(cond
((null? lat) #t)
((member? (car lat) (cdr lat)) #f)
(else (set? (cdr lat))))))
(print (set? '(apple peaches apple plum)))
(print (set? '(apples peaches pears plums)))
(print (set? '(apple 3 pear 4 9 apple 3 4)))
実行:
^o^ > gosh set.scm
#f
#t
#f
うまくいった。と思ったら、ページ(p.133)の下のほうのやり取りで、「これは次のデータに対してもうまく動きますか。 (apple 3 pear 4 9 apple 3 4)」「はい、member? は eq? ではなく equal? を使って書かれていますから。」とある。いつのまに書き直したんだ、と思って本を読み直したら、5章の最後のやり取りで書き直したことになっているらしい(ほかに該当するところが見当たらない)。この本では eq? は数ではないアトムを引数にとることになっている(eq? の掟)。
とはいえ、実際には eq? は引数が数でも正しく動くので、本と実際とでくい違ってしまっているわけだ。ま、いいか。一応、equal? を使った member? を載せておく。
(use mymodule)
(define member?
(lambda (a lat)
(cond
((null? lat) #f)
(else (or (equal? (car lat) a) (member? a (cdr lat)))))))
これも mymodule.scm に追加しておこう。
makeset
次はリストからセットを作る makeset。
(use mymodule)
(define makeset
(lambda (lat)
(cond
((null? lat) (quote ()))
((member? (car lat) (cdr lat)) (makeset (cdr lat)))
(else (cons (car lat) (makeset (cdr lat)))))))
(print (makeset '(apple peach pear peach plum apple lemon peach)))
実行:
^o^ > gosh -I. makeset.scm
(pear plum apple lemon peach)
OK。では次に multirember を使って makeset を書け、と。この multirember も equal? を使って書くみたいだな。
(use mymodule)
(define multirember
(lambda (a lat)
(cond
((null? lat) (quote ()))
((equal? (car lat) a) (multirember a (cdr lat)))
(else (cons (car lat) (multirember a (cdr lat)))))))
(define makeset
(lambda (lat)
(cond
((null? lat) (quote ()))
(else (cons (car lat) (makeset (multirember (car lat) (cdr lat))))))))
(print (makeset '(apple peach pear peach plum apple lemon peach)))
^o^ > gosh -I. makeset2.scm
(apple peach pear plum lemon)
うまく動いた。けど、この実装での結果が本(p.144)と違っている。たぶん間違っているのは本のほう。だって、makeset の2つの定義では違う動作をするはずなのに、本では結果が同じになってるから。