構文解析
関数の構文解析を1度だけ実行し、内部形式にして保存する。PascalのP形式みたいなもの。関数の呼び出しは、内部形式のほうを呼ぶ。解析の手間が減って速くなる。
では、Schemeの関数の内部形式はどんな形をしているのか?どこに保存しているのか?残念ながら、この章ではこれらの具体的な説明はない。ここでは、子Schemeの内部形式は、親Schemeの内部形式をそのまま流用しているだけである。
Ex. 4.22
let の導入。(内部define の対応、名前つき let などの変更する前のシンプルな let)
まず analyze の変更
(define (analyze exp) (cond ((self-evaluating? exp) ... ((if? exp)(analyze-if exp)) ((let? exp)(analyze-let exp)) ;; let 節追加 ...
追加した関数
(define (analyze-let exp) (let ((bindings (cadr exp)) (body (cddr exp))) (analyze (cons (cons 'lambda (cons (map car bindings) body)) (map cadr bindings)))))
Ex. 4.23
並びが一つの式 exp1 の場合、Alyssa の analyze-sequence の返す値は以下のとおり。
(lambda (env) (execute-sequence exp1 env))
このうち、先頭の lambda と exp1 は構文解析済みになる。しかし、execute-sequence 関数の部分は構文解析されない。そのまま、lambda のなかに保存される。lambda の本体は解析されないからである。ということで未解析の部分式が残ってしまう。(解析と評価をごっちゃにしないこと。)
同じく、本文中の analyze-sequence の返す値は以下のとおり。
exp1
exp1 は構文解析済みである。
並びが2つの式 exp1, exp2 の場合、Alyssa の analyze-sequence の返す値は以下のとおり。
(lambda (env) (execute-sequence (exp1 exp2) env))
このうち、先頭の lambda と exp1 と exp2 は構文解析済みになる。が、execute-sequence 関数の部分が構文解析されないまま残り、lambda のなかに保存される。
同じく、本文中の analyze-sequence の返す値は以下のとおり。
(lambda (env) (exp1 env) (exp2 env))
exp1, exp2 と先頭の lambda は構文解析済みになる。式は、部分式も含めて、全て解析済みになる。
Ex. 4.24
1~n の和を求める関数を使って速度を比べる
(define (f n) (if (= n 1) 1 (+ (f (- n 1)) n)))
(f 1000000) の実行時間
内部形式使わない版 15秒
内部形式使う版 10秒
すこしだけ、内部形式を使うと速くなる