在正常的评估规则下,(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 会做同样的事情(尽管目前还没有流行的)。考虑到书中的实际问题,我想我有点切题了,但希望你对如何判断评估的内容和时间以及它可以产生的影响有所了解。