海野秀之(うんのひでゆき)の外部記憶
Twitter (twilog) / RSS / アンテナ / ぶくま
3imp 4.5 節版処理系に改造をくわえていく。
普通に考えると、改造前の処理系について完全に理解してから、改造方針をたてて… とやるべきなのかも知れませんが、ここは触りながら理解していく方法で。
ひとまず、今回改造した版がこちら: sbscm-1.0.3.tar.bz2
Global 変数へのアクセス (define, set! および参照)をサポートし、 繰り返し VM を呼び出したときにグローバル変数が引き継がれるようにしました。
いくつかの組込み変数(四則と比較あたり)も追加してみました。
動かしてみた例:
Stack-Based Scheme Version 1.0.3 based on 3imp Section 4.5. >>> (+ 1 2) (frame (halt) (constant 2 (argument (constant 1 (argument (refer-global + (apply))))))) ==> 3 >>> (define x 1) (constant 1 (define-global x (halt))) ==> x >>> x (refer-global x (halt)) ==> 1 >>> (set! x (call/cc (lambda (cont) (cont 20)))) (frame (assign-global x (halt)) (conti (argument (close 0 (frame (return 1) (constant 20 (argument (refer-local 0 (apply))))) (apply))))) ==> 20 >>> (define fib (lambda (n) (if (<= n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (close 0 (frame (test (constant 1 (return 1)) (frame (return 1) (frame (argument (frame (argument (refer-global + (apply))) (frame (argument (refer-global fib (apply))) (constant 1 (argument (refer-local 0 (argument (refer-global - (apply))))))))) (frame (argument (refer-global fib (apply))) (constant 2 (argument (refer-local 0 (argument (refer-global - (apply)))))))))) (constant 1 (argument (refer-local 0 (argument (refer-global <= (apply))))))) (define-global fib (halt))) ==> fib >>> (fib 5) (frame (halt) (constant 5 (argument (refer-global fib (apply))))) ==> 8 >>> (fib 20) (frame (halt) (constant 20 (argument (refer-global fib (apply))))) ==> 10946
Global 変数は、e (environment) とは別のハッシュに格納している。 いまのところ、VM しかそのハッシュの中身を見ていないけど、define が組込み関数や syntax extension を隠す挙動を実現しようとすると、compile も global 変数を見ていないといけないような 気がするんだが。
…だいたい想像がつくような気もするけど、いまいちわかってないかも。 (だからこそ、作りながら理解する方針)
第一級の継続があるときの、グローバル変数の扱いがまちがっている気がする。
http://practical-scheme.net/wiliki/wiliki.cgi?Scheme%3a%E4%BD%BF%E3%81%84%E3%81%9F%E3%81%84%E4%BA%BA%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E7%B6%99%E7%B6%9A%E5%85%A5%E9%96%80#H-eivjo5zmspt にある環境破壊実験をやってみる。<br><br>Stack-Based Scheme Version 1.0.4 based on 3imp Section 4.5.<br>>>> (define k #f)<br>(constant #f (define-global k (halt)))<br><br>==> k<br><br>>>> (define x 1)<br>(constant 1 (define-global x (halt)))<br><br>==> x<br><br>>>> (+ x (call/cc (lambda (cont) ((set! k cont) 2))))<br>(frame (halt) (frame (argument (refer-global x (argument (refer-global + (apply))))) (conti (argument (close 0 (frame (return 1) (constant 2 (argument (refer-local 0 (assign-global k (apply)))))) (apply))))))<br><br>==> 3<br><br>>>> (k 20)<br>(frame (halt) (constant 20 (argument (refer-global k (apply)))))<br><br>==> 21<br><br>>>> (set! x 10)<br>(constant 10 (assign-global x (halt)))<br><br>==> 10<br><br>>>> (k 2)<br>(frame (halt) (constant 2 (argument (refer-global k (apply)))))<br><br>==> 12<br><br>継続なのに、環境破壊後の値を参照してしまっている(そりゃ、ああいう風にしたから当たり前なんですが)。<br><br>やっぱりだめです。
+ が任意個の引数を取ってくれないので、リンク先とまったく同じ例はためせないという問題もあり。
Gauche では、(+ x (call/cc (lambda (cont) cont 2))) とかけるな。<br><br>(cont 2) じゃなくてもいいのか。
一個前のコメントに関連して:<br><br>gosh> (begin + 1 2)<br>2<br><br>そうなの。<br><br>R5RS 読んでみたけど、そうなる理由がいまいちわかんなかった。<br>(たぶん、読めてない)
いろいろ誤読してたっぽい。