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中的懒惰和严格:)