根据正常评估规则,(p)
将通过调用函数p
而不带任何参数进行评估。例如(在Common Lisp中):
> (defun p()
5)
=> P
> (p)
=> 5
你的问题开始时提到了一种被称为“懒惰评估”的事情。 Common Lisp默认不会这样做;它从左向右评估函数的所有参数。计划没有规定他们的评估顺序,只是他们会这样做。
但是,在评估事物之前,需要对它们进行扩展(这可能意味着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也会这样做(尽管目前还没有流行的)。考虑到本书中的实际问题,我想我会进一步讨论一下,但希望你对如何评估什么是评估什么,什么时候什么,以及它可以产生什么差异有了一些想法。
这就是我错过的观点!预测'(= 0 0)'决定仅评估后续部分,忽略'(p)'的扩展。 – Javran