SCHEMEのdo文の動きを検証してみる

先日のこちらのエントリー。
2012-06-28 - 氏家備忘録/非実用日記群
気になって夜も眠れません(懐かしい響きだが、単なる常套句)。
そこで、do文がどうlambda式に展開されるか、自分なりに妄想してみました。

do文

(do ((v1 init1 next1) ... (vn initn nextn))
    (p r)
    body)

こんな感じでdo文は書きますよね。
これを少し変形してみます。

named-letに変換してみる。

(let loop ((v1 init1) ... (vn initn))
  (if p
      r
      (begin
        body
        (loop next1 ... nextn))))

loopというダミーのシンボルをつかってループさせます。

lambda文に落とし込んでみる。

再起させる必要があるので、ちゃんとlambdaだけで書くとすればちょっと面倒だけど、set!を使ってごまかす。

((lambda (loop)
  (set! loop
    (lambda (v1 ... vn)
      (if p
          r
          (begin
            body
            (loop next1 ... nextn)))))
  (loop init1 ... initn))
  '())

loopシンボルをとりあえず作って、あとからlambdaクロージャをset!してやる。
ちゃんと書こうとするとY-Combinatorだかなんだか使うんだよね確か(うろ覚え)。

件のdo文の例はどうなるの?

(do ((x 0 (+ x 1))
     (r '() (lambda () x)))
    ((= x 1) (r))
  )

ってな式だったから、上の変形に合わせてやれば次の通りlambdaに落とし込める。

((lambda (loop)
  (set! loop
    (lambda (x r)
      (if (= x 1)
          (r)
          (begin
            (loop (+ x 1) (lambda () x))))))
  (loop 0 '()))
  '())

とりあえず動きそう。

自作のSCHEMEON.jsでは果たしてどうなるか!

少しずつちまちまと改良しているSCHEMEON.js。
do文の例そのままだと誤った答えを返してくれるが、lambdaに落とし込んだ例だとちゃんと帰ってくれるはず!

((lambda (loop)
  (set! loop
    (lambda (x r)
      (if (= x 1)
          (r)
         (begin
            (loop (+ x 1) (lambda () x))))))
  (loop 0 '()))
  '())
->
0

おお、ちゃんと0を返してくれた。よしよし。
根っこが間違っているわけではなさそうだなSCHEMEON.js。
do文をあとで手直しします。いつかいつの日か。