元循环评估器与分析(元循环)评估器之间的区别
Difference between Metacircular Evaluator and the Analyzing (Metacircular) Evaluator
在section 4.1 of the Structure and Interpretation of Computer Programs中引入了两个评估器,分别是元循环评估器和分析元循环评估器。
完整的元循环求值器可以在这里找到:
https://mitpress.mit.edu/sicp/code/ch4.scm
完整的元循环求值器和分析可以在这里找到:
https://mitpress.mit.edu/sicp/code/ch4-analyzingmceval.scm
现在,两者的区别在于分析求值器eval
有分析表达式的分析方法。
(define (eval exp env)
((analyze exp) env))
(define (analyze exp)
(cond ((self-evaluating? exp)
(analyze-self-evaluating exp))
((quoted? exp) (analyze-quoted exp))
(...)
分析程序在哪里,例如:
(define (analyze-self-evaluating exp)
(lambda (env) exp))
或者这个:
(define (analyze-quoted exp)
(let ((qval (text-of-quotation exp)))
(lambda (env) qval)))
而元循环求值器 eval
这样做:
(define (eval exp env)
(cond ((self-evaluating? exp) exp)
(...)
(define (text-of-quotation exp) (cadr exp))
我希望分析评估者做的是在环境中的某个地方缓存以前分析的结果,但我没有看到这一点。我不明白分析评估者到底做了什么。
那么,分析求值器到底做了什么,为什么它会稍微加快求值器的速度?
如果仔细观察 analyze-something
函数,您会发现它们中的每一个(analyze-self-evaluating
和 analyze-variables
的一部分)执行一些操作,然后 returns一个lambda
。例如:
(define (analyze-if exp)
(let ((pproc (analyze (if-predicate exp)))
(cproc (analyze (if-consequent exp)))
(aproc (analyze (if-alternative exp))))
(lambda (env)
(if (true? (pproc env))
(cproc env)
(aproc env)))))
而在另一个解释器中,我们有:
(define (eval-if exp env)
(if (true? (eval (if-predicate exp) env))
(eval (if-consequent exp) env)
(eval (if-alternative exp) env)))
这里发生了什么?在第二种情况下,每次对同一个表达式求值(例如,因为我们多次执行递归函数的主体),都会在表达式上调用 eval-something
版本,然后再次对所有内容求值,包括 analyzing-something
对应方在返回函数之前执行的事情!
第一种情况,分析函数的第一部分只执行一次,执行多次的是它返回的过程。
如果您特别考虑 definition
案例,这一点应该很清楚。比较两个函数:
(define (analyze-definition exp)
(let ((var (definition-variable exp))
(vproc (analyze (definition-value exp))))
(lambda (env)
(define-variable! var (vproc env) env)
'ok)))
和
(define (eval-definition exp env)
(define-variable! (definition-variable exp)
(eval (definition-value exp) env)
env)
'ok)
在第二个中,函数在 eval
内部调用时每次 计算形式 (definition-value exp)
:
...
((definition? exp) (eval-definition exp env))
...
第一种情况,相反,你可以看到在函数的let
部分调用了(analyze (definition-value exp))
,那么它的结果就是stored 到 var 中,这样它就不会再被计算了。
在section 4.1 of the Structure and Interpretation of Computer Programs中引入了两个评估器,分别是元循环评估器和分析元循环评估器。
完整的元循环求值器可以在这里找到: https://mitpress.mit.edu/sicp/code/ch4.scm
完整的元循环求值器和分析可以在这里找到: https://mitpress.mit.edu/sicp/code/ch4-analyzingmceval.scm
现在,两者的区别在于分析求值器eval
有分析表达式的分析方法。
(define (eval exp env)
((analyze exp) env))
(define (analyze exp)
(cond ((self-evaluating? exp)
(analyze-self-evaluating exp))
((quoted? exp) (analyze-quoted exp))
(...)
分析程序在哪里,例如:
(define (analyze-self-evaluating exp)
(lambda (env) exp))
或者这个:
(define (analyze-quoted exp)
(let ((qval (text-of-quotation exp)))
(lambda (env) qval)))
而元循环求值器 eval
这样做:
(define (eval exp env)
(cond ((self-evaluating? exp) exp)
(...)
(define (text-of-quotation exp) (cadr exp))
我希望分析评估者做的是在环境中的某个地方缓存以前分析的结果,但我没有看到这一点。我不明白分析评估者到底做了什么。
那么,分析求值器到底做了什么,为什么它会稍微加快求值器的速度?
如果仔细观察 analyze-something
函数,您会发现它们中的每一个(analyze-self-evaluating
和 analyze-variables
的一部分)执行一些操作,然后 returns一个lambda
。例如:
(define (analyze-if exp)
(let ((pproc (analyze (if-predicate exp)))
(cproc (analyze (if-consequent exp)))
(aproc (analyze (if-alternative exp))))
(lambda (env)
(if (true? (pproc env))
(cproc env)
(aproc env)))))
而在另一个解释器中,我们有:
(define (eval-if exp env)
(if (true? (eval (if-predicate exp) env))
(eval (if-consequent exp) env)
(eval (if-alternative exp) env)))
这里发生了什么?在第二种情况下,每次对同一个表达式求值(例如,因为我们多次执行递归函数的主体),都会在表达式上调用 eval-something
版本,然后再次对所有内容求值,包括 analyzing-something
对应方在返回函数之前执行的事情!
第一种情况,分析函数的第一部分只执行一次,执行多次的是它返回的过程。
如果您特别考虑 definition
案例,这一点应该很清楚。比较两个函数:
(define (analyze-definition exp)
(let ((var (definition-variable exp))
(vproc (analyze (definition-value exp))))
(lambda (env)
(define-variable! var (vproc env) env)
'ok)))
和
(define (eval-definition exp env)
(define-variable! (definition-variable exp)
(eval (definition-value exp) env)
env)
'ok)
在第二个中,函数在 eval
内部调用时每次 计算形式 (definition-value exp)
:
...
((definition? exp) (eval-definition exp env))
...
第一种情况,相反,你可以看到在函数的let
部分调用了(analyze (definition-value exp))
,那么它的结果就是stored 到 var 中,这样它就不会再被计算了。