8

我正在阅读 SICP 的以下部分

http://mitpress.mit.edu/sicp/full-text/book/book-ZH-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都会调用。

我的理解有什么问题?我将不胜感激任何反馈,谢谢!

4

3 回答 3

5

Gintautas 的回答是正确的,但也许可以举个例子。假设你开发了一个带有循环结构的 Scheme 方言

(do-n-times n expr)

具有明显的语义。现在,当你调用 naiveeval来评估一个运行十次的循环时

(eval '(do-n-times 10 (print 'hello)))

然后它将分析循环体十次。eval使用将分析与评估分开的版本,循环体是analyzed 一次,然后评估十次。

分析阶段返回一个过程,该过程在您的 Scheme 解释器中可能会很快,也可能不会很快。但是,它可以进行各种优化(死代码分析、JIT 编译为机器码等)。

于 2010-10-15T15:12:59.140 回答
5

确实,每次您eval使用程序代码作为参数调用时,都会调用语法评估器。但是,当该代码中的一个函数调用该代码中的另一个函数(或者,在最简单的情况下,它通过递归调用自身)时,内部apply将获取分析的表达式(最终是一个 lambda 函数)作为参数,而不是需要再次进行语法分析才能执行的代码块。

于 2010-10-13T21:22:22.133 回答
2

larsmans 的回答非常好。

作为补充答案,人们也可以将其视为提前传递参数analyze(environ)的一种咖喱形式。在 SICP 中,您可以阅读如下示例代码:eval(expr, environ)expr

(define (analyze-assignment exp)
  (let ((var (assignment-variable exp))
        (vproc (analyze (assignment-value exp))))
    (lambda (env)
      (set-variable-value! var (vproc env) env)
      'ok)))

当您看到 alet (([var] [preprocessed stuff]))时,表示预处理存储在一个闭包中,直到稍后传入时才需要它environ

于 2011-11-06T11:45:09.933 回答