A-normal form 再学習 その6 - Scheme VM
define の normalize を変えた。a-normalize-name ではなくて a-normalize-with-kが良い。
こうでないと inline 時の手続きの大きさが把握できない。
現状を整理。
- A-reduction が動くようになった。
- inline が正しく行われているかを見よう。
- 短めの手続き
簡単な inline 展開
(define (kar obj) (car obj)) (print (kar '(a . b)))
と思ったら quote の扱いがおかしい。
quote は value? => #t であること、normalize は (k m)を返すことを追加して対応。
ついでに
(beta-reduction '(car '(a . b)))
の結果がおかしい。
list ではないものに map を使っているからか。
(define (map-pair proc p) (let loop ([p p]) (cond [(null? p) '()] [(pair? p) (cons (proc (car p)) (loop (cdr p)))] [else (proc p)])))
自前で用意したけど標準手続きでありそうだよなあ。
inline 展開後
(define kar (lambda (G62) (car G62))) (let1 G63 (begin (car '(a . b))) (print G63))
不十分だが同じ動作をするコードが得られた。
この材料だけではなんとも考察しがたいのでもう一例
(define (kar obj) (car obj)) (print (kar (kar '((a . b) . c)))) => (define kar (lambda (G62) (car G62))) (let1 G63 (begin (car '((a . b) . c))) (let1 G64 (begin (car G63)) (print G64)))
気になるのは
- この文脈では begin は省略可能。(コンパイル結果には影響ないけど)
- A-reduction の結果 let1 を2つ経由しているのだけど遅くならない?
かな。速度は計測すれば良かろう。
named let のバグ
計測のために named let でループを作ったがコンパイルエラーになるバグがあったので修正。
named let の実行バグ
正しいコードなのに実行できない。これは VM のバグ。
スタックオーバーフローだった。 tail call がうまく行っていないな。
あああ。a-normalize の結果、tail call が tail call ではなくなってしまっているな。
(a-normalize '(begin (+ 1 2) (+ 2 3))) => (let1 G1066 (+ 1 2) (let1 G1068 (+ 2 3) (begin G1066 G1068)))
これはつまり、let1, let, begin, lambda などの最後の body は正規化しないのが良いということ?
進むにつれて全然自信がなくなる><。
落ち着け落ち着け。
正規化により末尾呼び出しであるという情報が失われるのが問題。現在のコードでこの問題が起こるのは begin のときだけ。
良く見たら begin の normalize は定義してなかった。
まずは安易に正規化しても何も変わらないとしてみようか。意図通り動いた。
末尾以外は正規化しても良いと思うが、そこは ToDo ということで。
計測
(define (kar obj) (car obj)) (let loop ([i 1]) (if (= i 10000) i (begin (kar (kar '((a . b) . c))) (loop (+ i 1)))))
を inline 展開したら 50% ほど速くなった。
すごくない?
というわけで単純な場合は良い成果が出ることが分かった。