insert-g(つづき)

subst

リストの中の old を new に置き換える subst は次のような定義だった。

(define subst
  (lambda (new old l)
    (cond
      ((null? l) (quote ()))
      ((eq? (car l) old) (cons new (cdr l)))
      (else (cons (car l) (subst new old (cdr l)))))))

これもやっぱり、前回の insertL や insertR とよく似ている。違うのは cond の2行目、((eq? …) の行だけだ。というわけで、subst のための関数 seqS を作り、insert-g を使って subst を定義せよ、と。
こんなんでどうだ。(ちなみに insert-g の定義は本のとおり test? を引数にとらずに eq? を使うようにした)

(define insert-g
  (lambda (seq)
    (lambda (new old l)
      (cond
        ((null? l) (quote ()))
        ((eq? (car l) old) (seq new old (cdr l)))
        (else (cons (car l) ((insert-g seq) new old (cdr l))))))))

(define seqS
  (lambda (new old l)
    (cons new l)))

(define subst (insert-g seqS))

(print (subst 'topping 'fudge '(ice cream with fudge for dessert)))
^o^ > gosh subst2.scm
(ice cream with topping for dessert)

OKだ。

yyy

今度は次のような関数 yyy が出てきた。

(define insert-g
  (lambda (seq)
    (lambda (new old l)
      (cond
        ((null? l) (quote ()))
        ((eq? (car l) old) (seq new old (cdr l)))
        (else (cons (car l) ((insert-g seq) new old (cdr l))))))))

(define seqrem
  (lambda (new old l)
    l))

(define yyy
  (lambda (a l)
    ((insert-g seqrem) #f a l)))

(print (yyy 'sausage '(pizza with sausage and bacon)))

関数 yyy の定義で insert-g に渡している関数 seqrem は、new や old を cons せずに l だけを返している。ということは、(eq? (car l)) だったとき、new も old も cons されないわけで、つまり yyy は old を削除する関数 rember だってことだ。

^o^ > gosh yyy.scm
(pizza with and bacon)

本では、「#f はどんな役割を果たしていますか。」と書いてあるけど、#f は seqrem の引数 new に当たるので、関数の中では何の役目も果たしていない。実際、#f を #t やほかのアトムに変えても yyy の動作は変わらない。ただの数合わせのためだけにあるわけだ。

前回と今回のまとめ

前回と今回で insertL、insertR、subst、rember という4つの関数が、insert-g とそれぞれの補助関数で定義できることを見てきた。全体的によく似た関数は、同じ部分は共通の関数を使い、違う部分だけをそれぞれの補助関数にして定義できる。これが抽象化というものの力だそうだ。

第9の戒律
新しき関数においては共通のパターンを抽象化すべし。