11

这个问题可以在这里找到。

在书中,我发现对正常订单评估的一种描述是:

“另一种评估模型在需要操作数的值之前不会评估操作数。相反,它会首先用操作数表达式替换参数,直到它获得仅包含原始运算符的表达式,然后执行评估。”

我还找到了另一个简短的描述:“完全扩展然后减少”。

在练习中,我认为 of 的定义p类似于(lambda () (p)),它永远不会扩展为原始运算符,因此永远不会终止。

然而,另一方面,在谷歌搜索这个问题的一些答案之后,似乎正常的订单评估应该终止,因为它只根据需要评估事情,实际上(p)不会被评估。

所以我认为“扩展”和“评估”之间肯定有一些区别,而解释器在这里所​​做的就是评估事物。

到底有什么区别,还是我错过了一些要点?

另一个问题:我应该说“(p)被评估为(p)”还是“(p)被扩展为(p)”?

4

2 回答 2

22

你是对的,如果要求评估,应用顺序评估器不会终止(p)。但是,手头的问题提到if具有特定的评估语义,该语义由应用程序和正常顺序评估器共享。具体来说,“假设无论解释器使用正常顺序还是应用顺序,特殊形式 if 的评估规则都是相同的:首先评估谓词表达式,结果决定是评估结果表达式还是替代表达式。”

练习的代码是

(define (p) (p))

(define (test x y)
  (if (= x 0)
    0
    y))

并且正在考虑的测试是

(test 0 (p))

正常顺序评估是“完全扩展然后减少”选项。在正序评估下,(test 0 (p)) 完全展开为

(test 0 (p)) ==
(if (= 0 0)
  0
  (p))

由于if具有上述语义,且展开式中的测试条件为(= 0 0),为真,正序求值器决定求值后件,即0,所以表达式的值为0

然而,使用应用顺序求值,求值的第一步(test 0 (p))是求值表达式test,0(p),然后用求值和产生的值调用“应用”,因此“应用”的值。由于 的评估不会完成,的评估也不会完成。test0(p)(p)(test 0 (p))

于 2013-04-17T01:05:48.283 回答
5

在正常的评估规则下,(p)将通过调用p不带参数的函数来评估。例如(在 Common Lisp 中):

> (defun p ()
    5)
  => P
> (p)
  => 5

您的问题首先提到了一种称为“惰性评估”的东西。Common Lisp 默认不这样做;它从左到右评估函数的所有参数。Scheme 没有指定它们被评估的顺序,只是它们将被指定。

然而,在可以评估事物之前,它们需要被扩展(这可能意味着 lisp 中的许多事物),这使 lisp 能够控制评估顺序。例如,p可能是一个宏。在这种情况下,正常的评估规则不一定适用。同样,在 Common Lisp 中:

> (defmacro p ()
    (print "I'm being expanded!") ; print on expansion
    (terpri) ; new line.
    `(progn (print "I'm being evaluated!") ; print on evaluation
            (terpri)
            5))
  => P

这进入读取评估打印循环。表达式被读取,然后展开,评估,然后打印。

> (p)
  I'm being expanded!
  I'm being evaluated!
  => 5

为了停止评估扩展,让我们把它放在一个 lambda 中。您会注意到它仍然打印扩展消息。

> (defvar foo (lambda () (p)))
  I'm being expanded!
  => COMPILED FUNCTION #<LAMBDA>

现在我们调用它,并对表单进行评估。

> (funcall foo) ; call the function 
  I'm being evaluated!
  => 5

您可以自行扩展宏调用。

> (macroexpand-1 '(lambda () (p)))
  I'm being expanded!
  => (lambda () (progn (print "I'm being evaluated!")
                       (terpri)
                       5))

Haskell 等语言默认具有惰性求值。在您引用的段落中,SICP 让您想象了一个懒惰的 lisp 版本。为了让这样一个 lisp 工作,并且只评估需要的东西,它不仅需要盲目地扩展和评估所有东西,直到它达到一个值(参见 SICP 中对替换模型的讨论),而是仅仅扩展事物和仅在特别要求其值时才评估事物。P您可以将其与上面的示例进行比较,在该示例中,我们在 lambda 表达式的主体中扩展了宏,FUNCALL在我们想要值的时候强制进行评估。稍后在 SICP 中,您将使用类似的技术来实现惰性列表。

正如我所说,Haskell 会自动完成这类事情,不难想象一个 lisp 会做同样的事情(尽管目前还没有流行的)。考虑到书中的实际问题,我想我有点切题了,但希望你对如何判断评估的内容和时间以及它可以产生的影响有所了解。

于 2013-04-16T14:27:00.067 回答