Schemeの課題をあの実装で の4

今回はmosh

mosh

インストールに関しては先日書いた(多少更新)。

今回の目的のためだけならば、特にSVN版を使用する必要は無い。リリース版(.tar.gzでダウンロードできるもの)は、Gauchemoshが無くてもビルドできる。

SICP ex 2.33

mapやappend、lengthを作る。これらを作るにあたって、以下のようなaccumulateを使用することとする。

(define (accumulate op initial sequence)
  (if (null? sequence)
      initial
      (op (car sequence) (accumulate op initial (cdr sequence)))))

ヒントとして与えられている2つの事例は重要。3つの課題はこれらのヒントの変形でしかない。

(accumulate + 0 '(1 2 3 4 5)) ; リストの総和
(accumulate cons '() '(1 2 3 4 5)) ; リストをそのまま返す

前者がなぜ総和を返すのかを考える。総和のaccumulateを展開すると次のようになる。

(+ 1 (+ 2 (+ 3 (+ 4 (+ 5 0)))))

これは確かにリストの総和をあらわしている。
mapは次のように作ることが出来る。

(define (my-map p sequence)
  (accumulate (lambda (x y) (cons (p x) y) ) '() sequence))

後者の例と比較するために、まず、consをlambdaで書き直してみる。

cons = (lambda (x y) (cons x y))

これを、my-mapで渡しているクロージャと比較すると、consのxをpで処理していることが分かる。
同様に、lengthも例からの類推で考えることが出来る。前者の例は、リストの要素がすべて1であったときだけlengthとして働くことがわかる。そのため、+のパラメタの片方を無理やり1にしてしまえばどのようなリストでもlengthとして働くことが分かる。

  1. をlambdaで書き直すと、
+ = (lambda (x y) (+ x y)) ;この+は複数のパラメタを取れない

となり、常に1だけ加えたければ、xを「無視」して1に置き換えてしまえば良い。基本的にlambdaに渡された引数はすべて消費しなくても良い。

(define (my-length sequence)
  (accumulate (lambda (x y) (+ 1 y)) 0 sequence))

xとyを逆にするとうまく行かない。
appendは後者の例から考える。

(define (myappend seq1 seq2)
  (accumulate cons seq2 seq1))

引数の順序に注意する。

moshでの検証

moshのように開発中の実装は、基本的にユーザのエラーに寛容でない*1ので、予め入力すべき式をファイルに保存しておくことが望ましい。
moshのREPLにはloadやload後の対話モード*2が無いので、テスト用の式もファイルに書きこんでおく。

(define (accumulate op initial sequence)
  (if (null? sequence)
      initial
      (op (car sequence) (accumulate op initial (cdr sequence)))))

(define (my-map p sequence)
  (accumulate (lambda (x y) (cons (p x) y) ) '() sequence))

(define (my-append seq1 seq2)
  (accumulate cons seq2 seq1))

(define (my-length sequence)
  (accumulate (lambda (x y) (+ 1 y)) 0 sequence))

;;; tests
(display (my-map (lambda (x) (+ 1 x)) '(1 2 3 4 5)))
(display "   (= (2 3 4 5 6))")
(newline)
(display (my-append '(1 2 3 4 5) '(6 7 8 9 10)))
(display "   (= (1 2 3 4 5 6 7 8 9 10))")
(newline)
(display (my-length '(1 "two" "three" 4 #f)))
(display "   (= 5)")
(newline)

以上をtest.scmとして保存したのなら、

mosh -5 test.scm

として実行できる。
moshR6RSインタプリタなので、式を並べただけのファイルを直接実行することはできない(どの語彙を使用するかの指定が先頭に必要)。-5はR5RS相当のトップレベルで式を逐次実行するためのオプションとなる。

*1:要するに(read "error")のような単純なエラーでも処理系が落ちる

*2:goshならば-l,ypsilonならば-iオプション