Scheme で append 手続きを再実装(復習)

Scheme で append 手続きを再実装(完成) - satosystemsの日記で、コメントで教えてもらった append の実装を解析。

(define append
  (lambda args
    (let f ((ls '()) (args args))
      (if (null? args)
        ls
        (let g ((ls ls))
          (if (null? ls)
            (f (car args) (cdr args))
            (cons (car ls) (g (cdr ls)))))))))

たった 9 行のコードなのに、スラスラ頭に入ってこない。Java に直そうと思ったけど、それすらできなかった。

このコードで勉強になったことをまとめておこう。

学んだこと1

僕の理解だと、可変長引数は (x . y) のような書き方をしなきゃいけないはずなんだけど、なんでこの手続きはひとつしか書かれていないのか。

display で見てみたら理解できた。

(append '(a b) '(c d)) という呼出しだと args は ((a b) (c d)) になる。なるほど、これを知っていれば、別のロジックで組んでいたなぁ。見返してみたら R5RS にもちゃんと書いてあった。コード例がないから理解できていないだけだった。

学んだこと2

最後の行で g を呼び出して再帰している部分は、ls をひとつずつ減らしながら逐次処理をしつつ、全部なくなったら args からひとつ持ってきてそれを ls にして・・・、という再帰になっていて、見事に再帰的なリスト処理をやってのけている。

今の僕ではどれだけ時間をかけて考えてもこのコードにはたどり着かない。

学んだこと3

f とか g とか、結構いい加減な命名なのか。それとも慣習なんだろうか。

いずれにせよ、短い名前は良いことだ、ということがわかった。僕もこれから名前付き let では f とか g とかを使おう。