重新定义`if`时无休止的递归,为什么呢?

时间:2019-06-20 16:14:15

标签: scheme conditional-statements lisp infinite-loop evaluation

我是编程的新手,开始学习Language Scheme。 (我研究《计算机程序的结构和解释》这本书)发现了一个任务,下面将对此进行介绍。我编写了两个代码,将if替换为cond,但是由于某些原因,运行第一个代码时会无休止地递归,但是运行第二个代码时会无休止地递归,并且通常会计算 sqrt ...尽管代码相同,为什么会这样?

  

Alyssa P. Hacker看不到为什么需要以特殊形式提供if。她问:“为什么我不能仅仅根据cond将其定义为一个普通过程?”阿丽莎的朋友伊娃·卢·阿托(Eva Lu Ator)声称确实可以做到这一点,并且她定义了if的新版本:

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

Eva演示了Alyssa的程序:

(new-if (= 2 3) 0 5)
5

(new-if (= 1 1) 0 5)
0
  

高兴的是,Alyssa使用new-if重写了square-root程序:

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

当Alyssa尝试使用它来计算平方根时会发生什么?解释。

第一个代码:

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

(define (square x)
  (* x x))

(define (average x y)
  (/ (+ x y) 2))

(define (improve guess x)
  (average guess (/ x guess)))

(define (better-good-enough? prev-guess guess)
  (< (abs (- guess prev-guess))
     0.00001))

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

(define (better-sqrt x)
  (better-sqrt-iter 0 1.0 x))

第二个代码:

(define (square x)
  (* x x))

(define (average x y)
  (/ (+ x y) 2))

(define (improve guess x)
  (average guess (/ x guess)))

(define (better-good-enough? prev-guess guess)
  (< (abs (- guess prev-guess))
     0.00001))

(define (better-sqrt-iter prev-guess guess x)
  (cond ((better-good-enough? prev-guess guess)
      guess)
        (else (better-sqrt-iter guess
                        (improve guess x)
                        x))))

(define (better-sqrt x)
  (better-sqrt-iter 0 1.0 x))

2 个答案:

答案 0 :(得分:1)

这闻起来像是一个作业问题,但是,假设您使用的是Scheme,请考虑一下一般形式(因此,不是任何一种特殊形式)的情况会发生什么

(f a b c)

被评估:

  1. fabc以未定义的顺序进行评估(甚至可能一次都评估);
  2. 应该作为函数的f的值(换句话说,对其求值的结果)应用于对ab和{{1 }};
  3. 该函数返回一个或多个值,这是表单的值。

当您使用此评估策略来评估c的第一版时会发生什么?您可以只用纸和笔进行一些评估,看看会发生什么。

为什么评估规则在这里是个问题?

答案 1 :(得分:0)

我们可以使用替代模型。

(better-sqrt 1) 
(better-sqrt-iter 0 1.0 1)
(cond
  ((better-good-enough? 0 1.0) 1.0)
  (else
   (better-sqrt-iter 1.0
                     (improve 1.0 1)
                     1))))
(cond
  (#f 1.0)
  (else
   (better-sqrt-iter 1.0
                     (improve 1.0 1)
                     1))))

(better-sqrt-iter 1.0
                  (improve 1.0 1)
                  1)
(better-sqrt-iter 1.0
                  1.0
                  1)
(cond
  ((better-good-enough? 1.0 1.0) 1.0)
  (else
   (better-sqrt-iter 1.0
                     (improve 1.0 1.0)
                     1))))
(cond
  (#t 1.0)
  (else
   (better-sqrt-iter 1.0
                     (improve 1.0 1.0)
                     1))))
1.0

现在让我们用new-if尝试一下:

(better-sqrt 1) 
(better-sqrt-iter 0 1.0 1)

方案说,您可以按任何顺序评估程序。我选择从右到左!

(new-if (better-good-enough? 0 1.0) 
        1.0
        (better-sqrt-iter 1.0
                          (improve 1.0 1)
                          1))
(new-if (better-good-enough? 0 1.0) 
        1.0
        (new-if (better-good-enough? 1.0 1.0) 
                1.0
                (better-sqrt-iter 1.0
                                  (improve 1.0 1)
                                  1)))
(new-if (better-good-enough? 0 1.0) 
        1.0
        (new-if (better-good-enough? 1.0 1.0) 
                1.0
                (new-if (better-good-enough? 1.0 1.0) 
                        1.0
                        (better-sqrt-iter 1.0
                                          (improve 1.0 1)
                                          1))))
(new-if (better-good-enough? 0 1.0) 
        1.0
        (new-if (better-good-enough? 1.0 1.0) 
                1.0
                (new-if (better-good-enough? 1.0 1.0) 
                        1.0
                        (new-if (better-good-enough? 1.0 1.0) 
                                1.0
                                (better-sqrt-iter 1.0
                                                  (improve 1.0 1)
                                                  1)))))
;; goes on like this forever!

请注意,我从来没有做过任何good-enough调用,因为在评估new-if的主体之前,所有3个表达式都需要完成,而内置 if首先评估测试表达式,然后根据评估测试表达式的结果,仅评估结果表达式或替代表达式中的一个,因此递归停止。