这个问题可以在这里找到.
在书中,我发现正常订单评估的一个描述是:
"另一种评估模型不会在需要它们的值之前评估操作数.相反,它首先将操作数表达式替换为参数,直到它获得仅涉及原始运算符的表达式,然后执行评估."
我还简要地找到了另一种描述:"完全扩展然后减少".
在练习中,我认为定义p是类似的(lambda () (p)),它永远不会扩展到原始操作符,因此永远不会终止.
然而,另一方面,在搜索到这个问题的一些答案之后,似乎正常的订单评估应该终止,因为它只根据需要评估事物,实际上(p)不会被评估.
所以我认为"扩展"和"评估"之间必然存在一些差异,而解释者在这里做的是评估事物.
究竟有什么区别,或者我错过了哪些观点?
另一个问题:我应该说" (p)被评估为(p)"还是" (p)被扩展为(p)"?
Jos*_*lor 15
你是对的,如果要求评估,申请订单评估员不会终止(p).然而,手头的问题提到了if具有特定评估语义的应用程序和正常顺序评估者共享的问题.具体来说,"假设解释器使用正常或适用顺序时,特殊形式的评估规则是否相同:首先评估谓词表达式,结果确定是否评估结果或替代表达式."
练习的代码是
(define (p) (p))
(define (test x y)
(if (= x 0)
0
y))
Run Code Online (Sandbox Code Playgroud)
正在考虑的测试是
(test 0 (p))
Run Code Online (Sandbox Code Playgroud)
正常订单评估是"完全扩展然后减少"选项.在正常订单评估下,(test 0 (p)) 完全扩展为
(test 0 (p)) ==
(if (= 0 0)
0
(p))
Run Code Online (Sandbox Code Playgroud)
由于if具有上述语义,并且扩展中的测试条件(= 0 0)是真的,正常顺序评估器确定评估结果,即0表达式的值是0.
然而,使用应用顺序评估,评估的第一步(test 0 (p))是评估表达式test,0和(p),然后使用通过评估和生成的值调用("应用",因此"应用")值.由于评估不完整,评估也不会.test0(p)(p)(test 0 (p))
在正常的评估规则下,(p)将通过p不带任何参数的函数进行评估。例如(在Common Lisp中):
> (defun p ()
5)
=> P
> (p)
=> 5
Run Code Online (Sandbox Code Playgroud)
您的问题首先提到了“惰性评估”。默认情况下,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
Run Code Online (Sandbox Code Playgroud)
这将进入“读取评估打印循环”。读取表达式,然后对其进行扩展,求值,然后进行打印。
> (p)
I'm being expanded!
I'm being evaluated!
=> 5
Run Code Online (Sandbox Code Playgroud)
要停止评估扩展,让我们将其放在lambda中。您会注意到它仍然会打印扩展消息。
> (defvar foo (lambda () (p)))
I'm being expanded!
=> COMPILED FUNCTION #<LAMBDA>
Run Code Online (Sandbox Code Playgroud)
现在我们将其称为表单,然后对其进行评估。
> (funcall foo) ; call the function
I'm being evaluated!
=> 5
Run Code Online (Sandbox Code Playgroud)
您可以自行扩展宏调用。
> (macroexpand-1 '(lambda () (p)))
I'm being expanded!
=> (lambda () (progn (print "I'm being evaluated!")
(terpri)
5))
Run Code Online (Sandbox Code Playgroud)
默认情况下,Haskell之类的语言具有惰性评估。在您引用的文章中,SICP让您想到了Lisp的一个懒惰版本。为了使这种轻描淡写起作用,并且仅评估所需的事物,它不仅需要盲目地扩展和评估所有事物,直到获得价值(请参见SICP中关于替代模型的讨论),而且还需要扩展事物和仅在明确要求事物的价值时评估事物。您可以将其与上面的示例进行比较,在该示例中,我们P在lambda表达式的主体中扩展了该宏,并FUNCALL在需要该值时强制进行了计算。在SICP的后面,您将使用类似的技术来实现惰性列表。
就像我说的那样,Haskell是自动执行这种操作的,不难想像Lisp也可以做到这一点(尽管目前还没有流行的Lisp)。考虑到书中的实际问题,我想我会比较切线,但是希望您对如何评估被评估的内容,何时评估以及可以产生的影响有所了解。