寻求对SICP练习1.5的一些解释

时间:2013-04-16 11:43:33

标签: lisp scheme sicp

可以找到问题here

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

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

我还简要地找到了另一种描述:“完全展开然后减少”。

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

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

所以我认为“扩展”和“评估”之间必然存在一些差异,而解释者在这里做的是评估事物。

究竟有什么不同,或者 有没有我错过的一点?

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

2 个答案:

答案 0 :(得分:10)

如果要求评估(p),那么申请订单评估员不会终止。但是,手头的问题提到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),这是正确的,正常顺序评估者决定评估结果,即{{1} },所以表达式的值是0

然而,使用应用订单评估,评估0的第一步是评估表达式(test 0 (p))test0,然后调用“apply”,因此“applicative” (p)的值与评估test和{0时产生的值{1}}。由于(p)的评估无法完成,因此(p)的评估也不会完成。

答案 1 :(得分:3)

在正常的评估规则下,(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中的替换模型的讨论),而只是扩展事物和只有在专门询问价值时才会评估。您可以将此与上面的示例进行比较,我们在lambda表达式的主体中展开宏P,当我们想要值时强制使用FUNCALL进行评估。稍后在SICP中,您将使用类似的技术来实现惰性列表。

正如我所说,Haskell会自动执行此类操作,并且不难想象一个lisp会做同样的事情(尽管目前还不流行)。考虑到书中的实际问题,我想我有点切线,但希望你能够对如何判断评估内容以及何时以及它可以产生的差异有所了解。