Ch1 题解
Ex 1.3
(define (square x) (* x x)) (define (square-larger-two-numbers a b c) (define (large-number a b) (if (> a b) a b)) (define (small-number a b) (if (< a b) a b)) (define (largest-number a b c) (large-number a (large-number b c))) (define (middle-number a b c) (small-number a (large-number b c))) (+ (square (largest-number a b c)) (square (middle-number a b c))) )
Ex 1.5
采用normal-order evaluation的解释器会将(test 0 (p))先展开为(if (= x 0) 0 (p)),之后判断x是否为0,本例中x为0,因此返回0,而不解析(p)。
而在application-order evaluation的解释器中,(test 0 (p))里的(p)会首先被求值并带入到test里作参数,如此一来程序就会陷入对p无限的调用之中。
Ex 1.6
对于“特殊的”(if predicate then-clause else-clause),应用序解释器在解释的过程中会首先求解predicate,如果predicate返回真,则执行then-clause,否则执行else-clause。在得到predicate的结果之前不会操作then-clause和else-clause。
而对于本题中定义的new-if:
(define (new-if predicate then-clause else-clause) (cond (predicate then-clause) (else else-clause)))
可以看到new-if实际上是一个过程(procedure),那么应用序解释器会首先将predicate、then-clause和else-clause这三个参数求解,然后代入到new-if里。
如果在then-clause或者else-clause中发生直接的或者间接的对new-if的递归调用,就会出现解释器陷入无限递归的情况。因为解释器首先将三个参数都进行求解,然后才进入cond这段代码,则递归发生在了判断递归条件之前,无法产生递归边界。
对应正则序的解释器,则会发生表达式无限展开的情况。
Ex 1.7
求(sqrt x)中用到的good-enough?在给定x过小或者过大时都会出现问题。
过小的情况
如果给定数据过小,比如good-enough?的阈值为0.0001,而要开平方根的数是0.00000001,那么在迭代进行到0.0078时,其平方为0.00006,已经小于阈值,而0.00000001本身就远小于阈值,两者之间的差值自然小于阈值,迭代结束。而显然0.00000001的平方根是0.0001,显然0.0078作为结果误差过大。
过大的情况
由于浮点数精度有限,当指数过大时,阈值条件将无法达到,从而陷入无限的迭代中。
比如对于1.6e151,当迭代进行到guess为4.0000000000000005e+075时,guess的平方为1.6000000000000006e+151,与1.6e151作差得到5.8e135,显然是不满足阈值条件的;拿现在的guess去做improve,我们看到(/ x guess)得到4e75,对4e75和4.0000000000000005e+075求平均数,由于浮点数精度有限,这个平均数无法表示成4.00000000000000025e+075,得到结果仍然是4.0000000000000005e+075,于是又进入下一次迭代。
关于浮点数的表示,参见IEEE 754。
新的策略
对于误差过大的问题,可以通过减小阈值来保证结果的精确度。但是阈值过小也会使计算收敛的时间变长,计算速度变慢。
对于数据过大导致无法终止迭代的问题,这里采用一种新的策略来实现good-enough?:不再检测guess的平方与给定值的差值大小是否和要求,而是去检测两次迭代所产生的guess之间的差值,当两次guess相差足够小时,就可以认为算法已经收敛。
代码如下:
(define (average x y) (/ (+ x y) 2)) (define (sqr x) (* x x)) (define (abs x) (if (< x 0) (- x) x)) (define (good-enough? guess1 guess2) (< (abs (- guess1 guess2)) 0.0001)) (define (improve x guess) (average guess (/ x guess))) (define (sqrt-iter x guess) (if (good-enough? (improve x guess) guess) guess ((lambda () (display guess) (newline) (sqrt-iter x (improve x guess)))))) (define (sqrt x) (sqrt-iter x 1.0)) (sqrt 1.6e151)
Ex 1.8
(define (cube x) (* x x x)) (define (square x) (* x x)) (define (abs x) (if (< x 0) (- x) x)) (define (cube-root x) (define (improve x guess) (/ (+ (/ x (square guess)) (* 2 guess)) 3)) (define (good-enough? x guess) (< (abs (- (cube guess) x)) 0.0001)) (define (cube-root-iter x guess) (if (good-enough? x guess) guess (cube-root-iter x (improve x guess)))) (cube-root-iter x 1.0)) (cube-root 27.0)