7章2節の練習問題

for-each-numberとmap-numbersは簡単。

(define for-each-numbers
  (lambda (proc l)
    (for-each proc (filter number?  l))))

(define map-numbers
  (lambda (proc l)
    (map proc (filter number? l))))

ですな。


これを、for-eachやmapを受け取ってfor-each-numbersやmap-numbersに相当する関数を返すnumbers-only関数を作れ、と。
これも簡単。

(define numbers-only
  (lambda (walker)
    (lambda (proc l)
      (walker proc (filter number? l)))))

関数を返すのでlambdaが1段増える。


さて、tree-walk関数(id:yagiey:20090206)の1つ目の引数に

(numbers-only map)

を渡しても、想定するように動かない。
filterがネストしたリストに対応していないため、例えば

gosh> ((numbers-only map) (lambda (n) n) '(a (1) ((b)) (((2)))))
()

ってなるんですな。これが根本的な原因。
だから、単純にfilterではなく、ネストしたリストに対応したfilter*を作って、filter*を使ってnumbers-onlyを書き換えればいいんじゃないだろうか。
ってことでfilter*とnumbers-only*を書いてみた。

(define filter*
  (lambda (proc l)
    (cond
     ((null? l) '())
     ((list? (car l)) (cons (filter* proc (car l)) (filter* proc (cdr l))))
     ((proc (car l)) (cons (car l) (filter* proc (cdr l))))
     (else (filter* proc (cdr l))))))

(define numbers-only*
  (lambda (walker)
    (lambda (proc l)
      (walker proc (filter* number? l)))))

これを使ってtree-walkを動かしてみると

gosh> (tree-walk (numbers-only* map)
                 (lambda (n) n)
                 '(a (1) ((b)) (((3))) ((((#f 4))))))
((1) (()) (((3))) ((((4)))))

だと。
うむ、うまくいってるんじゃないだろうか。


なんか、もっとエレガントな方法があるような気がするけど、とりあえずよしとする。