Lisp 寻求有关SICP练习1.5的一些解释
这个问题可以找到 在书中,我发现对正常顺序评估的一个描述是: 替代的求值模型在需要操作数的值之前不会求值。相反,它会先用操作数表达式替换参数,直到得到只包含基元运算符的表达式,然后执行求值 我还发现了另一个简短的描述:“完全展开,然后缩小” 在练习中,我认为Lisp 寻求有关SICP练习1.5的一些解释,lisp,scheme,sicp,Lisp,Scheme,Sicp,这个问题可以找到 在书中,我发现对正常顺序评估的一个描述是: 替代的求值模型在需要操作数的值之前不会求值。相反,它会先用操作数表达式替换参数,直到得到只包含基元运算符的表达式,然后执行求值 我还发现了另一个简短的描述:“完全展开,然后缩小” 在练习中,我认为p的定义类似于(lambda()(p)), 它永远不会扩展到基元运算符,因此永远不会终止 然而,另一方面,在谷歌搜索了这个问题的一些答案后,正常顺序的求值似乎应该终止,因为它只根据需要求值,实际上不会求值 > (funcall foo)
p
的定义类似于(lambda()(p))
,
它永远不会扩展到基元运算符,因此永远不会终止
然而,另一方面,在谷歌搜索了这个问题的一些答案后,正常顺序的求值似乎应该终止,因为它只根据需要求值,实际上不会求值
> (funcall foo) ; call the function
I'm being evaluated!
=> 5
所以我认为“扩展”和“评估”之间肯定有一些区别,解释器在这里所做的就是评估事物
到底有什么区别,或者
我有没有遗漏一些要点
另一个问题:我应该说“
(p)
被求值为(p)
”还是“(p)
被扩展为(p)
”?在正常求值规则下,(p)
将通过无参数调用函数p
进行求值。例如(在公共Lisp中):
您的问题首先提到了一种称为“惰性评估”的方法。默认情况下,Common Lisp不会执行此操作;它从左到右计算函数的所有参数。Scheme没有指定它们的求值顺序,只是指定它们将被求值的顺序
但是,在对事物进行评估之前,需要对它们进行扩展(这在lisp中可能意味着许多事物),这使lisp能够控制评估的顺序。例如,p
可以是宏。在这种情况下,正常的评估规则不一定适用。同样,在公共Lisp中:
> (defun p ()
5)
=> P
> (p)
=> 5
> (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>
您可以自行展开宏调用
> (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也会这样做(尽管目前还没有流行的lisp)。考虑到书中的实际问题,我想我说了一点相切的话,但希望您能对如何判断评估内容、评估时间以及它可能产生的差异有一点了解。您是对的,如果要求应用程序顺序评估器评估
(p)
,应用程序顺序评估器不会终止。然而,手头的问题提到,if
具有特定的求值语义,该语义由应用程序和正常顺序求值器共享。具体地说,“假设特殊形式if的求值规则相同,无论解释器使用的是正常顺序还是应用顺序:首先求值谓词表达式,结果确定是求值后续表达式还是替代表达式。”
练习中的代码是
(define (p) (p))
(define (test x y)
(if (= x 0)
0
y))
正在考虑的测试是
(test 0 (p))
正常顺序评估是“完全展开然后缩减”选项。在正常顺序求值下,(测试0(p))
完全展开为
(test 0 (p)) ==
(if (= 0 0)
0
(p))
由于如果
具有上述语义,并且扩展中的测试条件为(=0 0)
,这是真的,正常顺序计算器确定评估结果,即0
,因此表达式的值为0
然而,使用应用程序顺序求值,求值
(test 0(p))
的第一步是求值表达式test
、0
和(p)
,然后用求值0
和(p)
生成的值调用test
的值(“apply”,因此是“applicative”)。由于对(p)
的评估不会完成,对(测试0(p))
的评估也不会完成,这是我错过的一点!谓词(=0 0)
决定只对结果部分求值,跳过了(p)
的扩展。你的回答很有帮助,很尴尬,我不能同时接受两个答案。1.我可以说“扩展”是计算一个宏的过程,然后把结果放在调用宏的地方,当我们强制进行一些计算时,“计算”就完成了吗?2.很好的类比,正常秩序和应用秩序就像Haskell中的懒惰和严格:)