evens-only*&co

evens-only*

関数 evens-only* は入れ子になった数のリストからすべての奇数を削除する。「簡単な練習でしょう」なんて書いてあるので、今度も答を見ないで書いてみる。ちなみに、偶数であることを判定する even? の定義は質問の中で提示されている。

(use mymodule)

(define even?
  (lambda (n)
    (o= (o* (o/ n 2) 2) n)))

(define evens-only*
  (lambda (l)
    (cond
      ((null? l) (quote ()))
      ((atom? (car l))
        (cond
          ((even? (car l)) (cons (car l) (evens-only* (cdr l))))
          (else (evens-only* (cdr l)))))
      (else
        (cons (evens-only* (car l)) (evens-only* (cdr l)))))))

(print (evens-only* '((9 1 2 8) 3 10 ((9 9) 7 6) 2)))
^o^ > gosh -I. evens-only_star.scm
((2 8) 10 (() 6) 2)

うん、簡単。

evens-only*&co

こっちが本題。関数 evens-only*&co は、引数から奇数を削除して偶数だけの入れ子のリストを作る一方、同時に引数中の偶数の積と奇数の和を計算する関数。概要が示されているので、それをもとに書いてみよう・・・・・・と思ったけど、最後の else の再帰をどうすればわからなくて、結局答を見て写経した。

(use mymodule)

(define even?
  (lambda (n)
    (o= (o* (o/ n 2) 2) n)))

(define evens-only*&co
  (lambda (l col)
    (cond
      ((null? l) (col (quote ()) 1 0))
      ((atom? (car l))
        (cond
          ((even? (car l)) (evens-only*&co (cdr l)
            (lambda (newl p s) (col (cons (car l) newl) (o* (car l) p) s))))
          (else (evens-only*&co (cdr l)
            (lambda (newl p s) (col newl p (o+ (car l) s)))))))
      (else
        (evens-only*&co (car l)
          (lambda (al ap as)
            (evens-only*&co (cdr l)
              (lambda (dl dp ds)
                (col (cons al dl) (o* ap dp) (o+ as ds))))))))))

(define the-last-friend
  (lambda (newl product sum)
  (cons sum (cons product newl))))

(print (evens-only*&co '((9 1 2 8) 3 10 ((9 9) 7 6) 2) the-last-friend))

最後の else の再帰は、まず (car l) について evens-only*&co を再帰してその収集子の中でさらに (cdr l) について再帰している。(car l) についての収集子の引数 al、ap、ps は (car l) のなかの偶数のリスト、偶数の積、奇数の和。それから (cdr l) についての収集子の引数 dl、dp、ds は (cdr l) のなかの偶数のリスト、偶数の積、奇数の和だ。そして最後にリストは cons、積は o*、和は o+ をそれぞれ適用しているわけだ。ああ、なんてややこしいんだ。

実行結果:

^o^ > gosh -I. evens-only_star_and_co.scm
(38 1920 (2 8) 10 (() 6) 2)

はあ、なんとかできた。
これで8章も終わり。

multiinsertLR&co

multiinsertLR

関数 multiinsertLR は lat の中の oldL の左と oldR の右に new を挿入する。

(define multiinsertLR
  (lambda (new oldL oldR lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) oldL)
        (cons new (cons oldL (multiinsertLR new oldL oldR (cdr lat)))))
      ((eq? (car lat) oldR)
        (cons oldR (cons new (multiinsertLR new oldL oldR (cdr lat)))))
      (else
        (cons (car lat) (multiinsertLR new oldL oldR (cdr lat)))))))

(print (multiinsertLR 'salty 'fish 'chips '(chips and fish or fish and chips)))
^o^ > gosh multiinsertLR.scm
(chips salty and salty fish or salty fish and chips salty)

うん、うまくいってる。

multiinsertLR&co

さて、ここからが本題。multirember に対応する multirember&co のように、multiinsertLR に対応する multiinsertLR&co を書け、ときた。ようするに、引数を1つ多くとり、それは収集子(関数)だってことだ。で、multiinsertLR&co が実行されると、新たなラット、左挿入の回数、右挿入の回数を引数として収集子 col を呼び出す。
よし、挑戦してみよう。

(use mymodule)

(define multiinsertLR&co
  (lambda (new oldL oldR lat col)
    (cond
      ((null? lat) (col (quote ()) 0 0))
      ((eq? (car lat) oldL)
        (multiinsertLR&co new oldL oldR (cdr lat)
          (lambda (newlat l r) (col (cons new (cons oldL newlat)) (add1 l) r))))
      ((eq? (car lat) oldR)
        (multiinsertLR&co new oldL oldR (cdr lat)
          (lambda (newlat l r) (col (cons oldR (cons new newlat)) l (add1 r)))))
      (else
        (multiinsertLR&co new oldL oldR (cdr lat)
          (lambda (newlat l r) (col (cons (car lat) newlat) l r)))))))

(define result
  (lambda (lat l r)
    (list lat l r)))

(print (multiinsertLR&co 'salty 'fish 'chips '(chips and fish or fish and chips) result))

今回、col の具体的な実装は出てこないので、収集した結果(新しいラット、左挿入の回数、右挿入の回数)をリストにして返す result を収集子にしてみた。

^o^ > gosh -I. multiinsertLR_and_co.scm
((chips salty and salty fish or salty fish and chips salty) 2 2)

うまくいった!