76

我刚刚开始学习 SICP(我自己;这不是为了上课),而且我已经在练习 1.6 上苦苦挣扎了几天,我似乎无法弄清楚。这是 Alyssa 根据 重新定义if的地方cond,如下所示:

(define (new-if predicate then-clause else-clause)
    (cond (predicate then-clause)
          (else else-clause))

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

(define (sqrt-iter guess x)
    (new-if (good-enough? guess x)
            guess
            (sqrt-iter (improve guess x)
                       x)))

然后问题问:“当 Alyssa 尝试使用它来计算平方根时会发生什么?解释一下。” [如有必要,我很乐意重现其他程序(good-enough?improve等),请告诉我。]

现在,我知道会发生什么:它从不返回值,这意味着程序无限递归。我只是无法解释为什么会发生这种情况。无论我之间存在什么细微差别,ifnew-if都无法理解。非常感谢任何和所有帮助。

4

6 回答 6

101

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

于 2009-07-23T11:55:44.370 回答
55

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

普通人if无需先评估其论点,顺其自然,这就是 和 之间的if区别new-if。:)

于 2011-12-12T12:36:38.313 回答
28

首先,您必须了解应用订单评估和正常订单之间的区别。Lisp 使用应用顺序,但条件表达式的计算不像普通函数(sicp 第 1.1.6 章):

(if <predicate> <consequent> <alternative>)

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

于 2009-07-23T13:04:57.923 回答
11

在 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>)计算左→右。如果任何计算结果为假,则 and 表达式的值为假,并且<e>不计算其余的 's。
    • ifand这样的条件句cond
      • (if <predicate> <consequent> <alternative>):如果<predicate>评估为真值,则解释器评估<consequent>并返回其值。否则它评估<alternative>并返回它的值
      • (cond (<p1> <e1>) ... (<pn> <en>))<p1>:首先评估谓词。如果其值为假,则<pn>进行评估。如果<pn>的值也为假,则<pn+1>评估。当谓词为真时,解释器返回相应的结果表达式的值<e>

对于练习 1.6:

  • new-if是一个正常的程序。在 Scheme(和许多其他语言)中,在调用过程之前对参数进行全面评估。这称为应用顺序。∴sqrt-iter每次调用都会new-if被调用,导致无限循环。
  • 为了读者的启迪, normal ifs 是一种特殊的形式。除非调用 ,否则不会评估递归语句<alternative>
于 2020-01-21T02:10:37.070 回答
7

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

考虑这种差异的另一种方法是:使用在某个点停止的递归和使用永远循环的递归如何?ifnew-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)))

并且由于predicateis now true,解释器将评估consequent(which is guess),返回其值并且不再评估( alternativewhich is (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)))

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

于 2020-09-16T01:06:08.690 回答
0

例 1.6。新的如果:

(define (new-if predicate then-clause else-clause)
     (cond (predicate then-clause)
                (else else-clause)))

与“if-statements”的区别:if-statements 从谓词 -> 后件 -> 替代项一一评估,

然而,'new-if' 必须评估所有参数,也就是 MOMENT 它调用的参数(这意味着'else-clause'在开始时被评估!!),

因此,当这些参数中的任何一个将自己调用到迭代循环中时,这会导致无限循环

于 2018-09-30T08:21:02.683 回答