我正在阅读SICP的以下部分
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-26.html#%_sec_4.1.7
根据该文本,eval
的以下转换将改善提供性能,因为多次评估的表达式只会被分析一次?
(define (eval exp env)
((analyze exp) env))
这是本书中给出的analyze
函数:
(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)))))
我不明白为什么这本书说analyze
只运行一次。 eval
((analyze exp) env))
的正文基本上不是每次调用eval
,analyze
都会以exp
作为参数调用analyze
?这意味着每次调用eval
时都会调用{{1}}。
我的理解有什么问题?我要感谢任何反馈,谢谢!
答案 0 :(得分:5)
实际上,每次用程序代码作为参数调用eval
时,都会调用语法赋值器。但是,当该代码中的函数调用该代码中的另一个函数时(或者,在最简单的情况下,它通过递归调用自身),内部apply
将获得分析的表达式(最后是lambda函数) )作为一个参数,而不是需要再次进行语法分析以便执行的代码块。
答案 1 :(得分:5)
Gintautas的回答是正确的,但也许是一个例子。假设您已经开发了一个体育循环结构的Scheme方言
(do-n-times n expr)
具有明显的语义。现在,当您调用天真eval
来评估一个运行十次的循环时
(eval '(do-n-times 10 (print 'hello)))
然后它会分析循环体十次。使用eval
版本将分析与评估分开,循环体为analyze
d一次,然后评估十次。
分析阶段会返回一个过程,该过程在您的Scheme解释器中可能会或者不会很快。但是,可以想象它可以进行各种优化(死代码分析,机器代码JIT compilation等)。
答案 2 :(得分:2)
larsmans的答案非常好。
作为补充答案,您还可以将analyze(environ)
视为eval(expr, environ)
的咖喱形式,其中参数expr
已提前传递。在SICP中,您可以阅读示例代码,如:
(define (analyze-assignment exp)
(let ((var (assignment-variable exp))
(vproc (analyze (assignment-value exp))))
(lambda (env)
(set-variable-value! var (vproc env) env)
'ok)))
当您看到let (([var] [preprocessed stuff]))
时,预处理存储在闭包中,直到稍后在传递environ
时需要它。