理系学生日記

おまえはいつまで学生気分なのか

練習問題

久しぶりに。

数値とそれ以外のものが混ざっているリストを受け取って、その中の数値だけについて手続きを適用する手続きl for-each-numbers を書いてみてください。(中略)同様に、数値だけに手続きを適用してその結果をリストにする map-numbers も書いてみましょう。

(define (numbers-only proc-to-list)
  (lambda (proc lis)
    (proc-to-list proc (filter number? lis))))

(define (for-each-numbers proc lis)
  ((numbers-only for-each) proc lis))

(define (map-numbers proc lis)
  ((numbers-only map) proc lis))

これによって、

gosh> (for-each-numbers print '(1 2 #f 3 4 #t))
1
2
3
4
#<undef>

gosh> (map-numbers (lambda (x) (* x 2)) '(1 2 #f 3 4 #t))
(2 4 6 8)

みたいなことになる。

しかし、このままだとリスト内リストに対応できない。

(define (tree-walk walker proc tree)
  (walker (lambda (elt)
            (if (list? elt)
                (tree-walk walker proc elt)
                (proc elt)))
          tree))
gosh> (tree-walk (numbers-only for-each) (lambda (x) (print x)) '((1 2 #t) 4 5 (#f (7 #t))))
4
5
#<undef>

これは、当然ながら number? がリスト内リストに対して #f を返すため。
というわけで、filter をリスト内リストに対応できるように拡張してやる。

(define (numbers-only-tree-walk walker proc tree)
  (define (filter pred lis)
    (cond ((null? lis) ())
          ((list? (car lis))
              (cons (filter pred (car lis)) (filter pred (cdr lis))))
          ((pred (car lis))
              (cons (car lis) (filter pred (cdr lis))))
          (else
           (filter pred (cdr lis)))))
  (define (numbers-only proc-to-list)
    (lambda (proc lis)
      (proc-to-list proc (filter number? lis))))
  ((numbers-only walker) (lambda (elt)
                           (if (list? elt)
                               (numbers-only-tree-walk (numbers-only walker) proc elt)
                               (proc elt)))
   tree))
gosh> (numbers-only-tree-walk for-each (lambda (x) (print x)) '((1 2 #t) 4 5 (#f (7 #t))))
1
2
4
5
7
#<undef>

gosh>  (numbers-only-tree-walk map (lambda (x) (* x 2)) '((1 2 #t) 4 5 (#f (7 #t))))
((2 4) 8 10 ((14)))