SICP练习1.6的解释是什么?

Ale*_*son 58 recursion sicp

我刚刚开始通过SICP(我自己;这不适合一个班级),我已经在练习1.6中苦苦挣扎了几天,我似乎无法弄明白.这是Alyssa重新定义if的方式cond,如下所示:

(define (new-if predicate then-clause else-clause)
    (cond (predicate then-clause)
          (else else-clause))
Run Code Online (Sandbox Code Playgroud)

她在一些简单的情况下成功测试它,然后用它来重新编写平方根程序(它工作得很好if):

(define (sqrt-iter guess x)
    (new-if (good-enough? guess x)
            guess
            (sqrt-iter (improve guess x)
                       x)))
Run Code Online (Sandbox Code Playgroud)

然后问题是:"当Alyssa试图用它来计算平方根时会发生什么?解释." [如果有必要,我很高兴能重现其他程序(good-enough?,improve,等),只是让我知道.]

现在,我知道会发生什么:它永远不会返回一个值,这意味着程序无限地递归.我无法解释为什么会这样.无论之间存在什么微妙的差异,if并且new-if正在逃避我.任何和所有帮助非常感谢.

Gre*_*ill 77

new-if是一个功能.调用函数时,Scheme对参数列表的第一件事是什么?它评估所有参数.

  • OP 在 Haskell 中不会有这个问题!(他会有一大堆_其他_问题......) (3认同)

小智 37

new-if是一个程序,而Scheme使用了应用顺序评估(1.1.5),因此即使在new-if实际执行之前,它也必须首先评估所有参数,即guess(sqrt-iter (improve guess x) x).你可以看到后一个参数是一个递归,它调用一个新的new-if过程,这就是无限循环的发生方式.

普通if不必评价其论据第一,只是一路上去,这之间的区别ifnew-if.:)


ale*_*asi 23

首先,您必须了解应用订单评估与正常订单之间的区别.Lisp使用了应用顺序,但条件表达式的评估与普通函数不同(sicp chapter 1.1.6):

(if <predicate> <consequent> <alternative>)
Run Code Online (Sandbox Code Playgroud)

为了评估if表达式,解释器首先评估<predicate>表达式的一部分.如果<predicate>求值为true值,则解释器将计算<consequent>并返回其值.否则,它会计算<alternative>并返回其值.


sch*_*dde 9

在 Scheme 中可以通过三种方式评估表单:

  1. 适用顺序
    • 评估参数然后应用
    • 给定f(x)=x+x3*f(1)*f(1)3*2*2
  2. 正常顺序
    • 完全展开然后减少
    • 给定f(x)=x+x3*f(1)*f(1)3*(1+1)*(1+1)(也用于“惰性求值”)
  3. 特殊表格例如:
    • 布尔值andor. 例如:(and <e1> ... <en>)计算左→右。如果任何计算结果为 false,则 and 表达式的值为 false,并且<e>不计算 's的其余部分。
    • 条件像ifcond
      • (if <predicate> <consequent> <alternative>):如果<predicate>计算结果为真值,解释器然后计算<consequent>并返回其值。否则,它计算<alternative>并返回它的值
      • (cond (<p1> <e1>) ... (<pn> <en>))<p1>先评估谓词。如果其值为 false,则<pn>对其进行评估。如果<pn>的值也为假,则<pn+1>进行评估。当谓词为真时,解释器返回相应后续表达式的值<e>

对于练习 1.6:

  • new-if是正常程序。在 Scheme(和许多其他语言)中,在调用过程之前完全评估参数。这称为应用顺序。∴sqrt-iter每次被调用都会new-if被调用,导致无限循环。
  • 为了读者的启迪, normalif是一种特殊形式。除非<alternative>调用 ,否则不会评估递归语句。


sal*_*ico 5

以前的答案很棒。我将添加另一个以更彻底的方式解释的内容。

考虑这种差异的另一种方式是这样的:递归如何使用if在某个点停止,而递归如何使用new-if永远循环?

首先让我们看看这两个if是如何工作的,然后他们在这种情况下是如何工作的。

if

@alex-vasi 对此进行了解释:

要评估 if 表达式,解释器首先评估<predicate>表达式的一部分。如果<predicate>计算结果为真值,解释器然后计算<consequent>并返回它的值。否则,它会评估<alternative>并返回其值。

new-if

@Schmudde对此进行了解释:

在调用过程之前,所有参数都被完全评估

递归如何使用if在某个点停止?

它正在停止,因为在guess足够好的地方(即(good-enough? guess x)true),我们将有:

(if (good-enough? guess x)
    guess
    (sqrt-iter (improve guess x)
               x)))
Run Code Online (Sandbox Code Playgroud)

而且,由于predicate现在是true,解释器将评估consequent(这是guess),返回它的价值和将不再评估alternative(这是(sqrt-iter (improve guess x) x))。

所以if实际上(sqrt-iter (improve guess x) x)递归评估直到guess足够好。然后它停止递归。

递归如何使用new-if永远循环?

与 一样if, withnew-if (sqrt-iter (improve guess x) x)将被递归评估,直到guess足够好。

但随后它会(sqrt-iter (improve guess x) x)一次又一次地继续评估。为什么?因为在评估时:

(new-if (good-enough? guess x)
    guess
    (sqrt-iter (improve guess x)
               x)))
Run Code Online (Sandbox Code Playgroud)

由于new-if是一个过程,它不会检查是否(good-enough? guess x)为真以决定评估guess(sqrt-iter (improve guess x))。它的作用是,它会评估(good-enough? guess x)guess并且(sqrt-iter (improve guess x)),因为这些都是过程的参数。因此,即使guess足够好,它也会继续(sqrt-iter (improve guess x))递归调用:/。

  • 这就像答案的第二部分。谢谢! (2认同)