問題2-20

2〜3日前に取り組んだ時は、頭がこんがらがってしまって途中で断念してしまいましたが、id:higeponさんのエントリーを参考にしながら、落ち着いて再考してみました。
大前提として、偶数・奇数を判別する基本手続きがあります。

(even? 10)
(odd? 10)

=>#t
=>#f

まずは、引数として渡されたリストをそのままコピーすることから考えを進めてみようと思い、以下のようなmirror手続きを書いてみました。渡されたリストがnilだったらnilを返すような手続きです。

(define (mirror list)
  (if (null? list)
      '()
      (cons (car list)
            (mirror (cdr list)))))

(mirror (list 1 2 3 4 5 6 7))

=>(1 2 3 4 5 6 7)

次に、渡されたリストの中から、偶数の値だけを返すような手続きを書いてみました。even?/odd?の存在を忘れていたため、以前取り組んだ時は、偶奇判定だけでごちゃごちゃしたコードになってしまった記憶があります。また、手続き内部でselect-evenが二か所に存在しているのが気に入りませんが、今回は良しとすることにしました。

(define (select-even list)
  (if (null? list)
    '()
    (if (even? (car list))
        (cons (car list)
              (select-even (cdr list)))
        (select-even (cdr list)))))

(select-even (list 1 2 3 4 5 6 7))

=>(2 4 6)

さらに、偶奇判定手続きそのものを引数として渡す手続きを書きました。これによって、偶奇いずれかの要素だけを取り出せるようになります。

(define (select-parity p? list)
  (if (null? list)
      '()
      (if (p? (car list))
          (cons (car list)
                (select-parity p? (cdr list)))
          (select-parity p? (cdr list)))))

(select-parity even? (list 1 2 3 4 5 6 7))
(select-parity odd? (list 1 2 3 4 5 6 7))

=>(2 4 6)
=>(1 3 5 7)

問題2-20は、先頭引数と同じ偶奇の要素をリストとして返すような手続きですので、まずは偶奇判定をして、その結果に応じて、上記select-parityのようなメソッドを呼び出すようにします。

(define (same-parity . init)
  (define parity-check
    (if (even? (car init))
        even?
        odd?))
  (define (iter p? list)
    (if (null? list)
        '()
        (if (p? (car list))
            (cons (car list)
                  (iter p? (cdr list)))
            (iter p? (cdr list)))))
  (iter parity-check init))

(same-parity 1 2 3 4 5 6 7)
(same-parity 2 3 4 5 6 7 8)

=>(1 3 5 7)
=>(2 4 6 8)

とりあえず上手くいって良かったです。手続きを引数として渡せるという概念が、まだまだ頭の中に染み付いていないことを痛感させられた問題だったような気がしています。