习题解答

时间:2022-07-23
本文章向大家介绍习题解答,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

练习 1.1

题目:下面是一系列表达式,对于每个表达式,解释器将输出什么结果?假定这一系列表达式是按照给出的顺序逐个求值的。

(define a 3)
(define b (+ a 1))
(+ a b (* a b))
(= a b)
(if (and (> b a) (< b (* a b)))
    b
    a)

(cond ((= a 4) 6)
      ((= b 4) (+ 6 7 a))
      (else 25))

(+ 2 (if (> b a) b a))

(* (cond ((> a b) a)
         ((< a b) b)
         (else -1))
   (+ a 1))

注:有些表达式过于简单,这里只保留了一些稍有难度的表达式。

解答

按照前两行得出 a=3b=4,之后的表达式逐个求值即可。

19
#f
4
16
6
16

练习 1.2

解答

(/ (+ 5
      4
      (- 2 (- 3 (+ 6 (/ 4 5)))))
   (* 3
      (- 6 2)
      (- 2 7))

练习 1.3

题目:定义一个过程,以三个数字为参数,返回其中两个较大数字的平方和。

解答

分三种情况分析,找出最小的数,剩下两个数求平方和即可。

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

(define (sumsquares x y) (+ (square x) (square y)))

(define (sqsumlargest a b c)
  (cond
    ((and (>= a c) (>= b c)) (sumsquares a b))
    ((and (>= b a) (>= c a)) (sumsquares b c))
    ((and (>= a b) (>= c b)) (sumsquares a c))))

注:Scheme 有两个常量 #t#f。解释器将 #f 解析为 false,任意其他值都作为 true 来处理,#t 并不是必要的,只是为了方便。

练习 1.4

题目:我们的求值模型允许组合式的操作符是复合表达式,基于这一思想对下列过程的行为进行描述。

(define (a-plus-abs-b a b)
  ((if (> b 0) + -) a b))

解答

 (a-plus-abs-b 1 -3)
 ((if (> -3 0) + -) 1 -3)
 ((if #f + -) 1 -3)
 (- 1 -3)
 4
  
 (a-plus-abs-b 1 3)
 ((if (> 3 0) + -) 1 3)
 ((if #t + -) 1 3)
 (+ 1 3)
 4

练习 1.5

题目:Ben 先生发明了一种测试方法,能够确定其使用的解释器采用的是应用序还是正则序求值。他定义了如下两种过程:

(define (p) (p))
(define (test x y)
  (if (= x 0) 0 y))

然后他对下面的表达式求值:

(test 0 (p))

如果解释器采用的是应用序求值,Ben 先生会观察到什么情况?如果采用的是正则序求值,又会是什么情况?(假定对特殊形式 if 的求值规则对两种序都是一样的,即先分析谓词,再根据结果决定分析哪个表达式)

解答

对于应用序求值,由于会先对参数求值,所以会先去求值 (p),导致无限循环,程序卡死:

(test 0 (p))

(test 0 (p))

(test 0 (p))

对于正则序求值,由于不会先对参数求值,而先执行复合过程 test,由于 if 判断结果为真,所以求值第一个表达式,得到结果为 0.

(test 0 (p))

(if (= 0 0) 0 (p))

(if #t 0 (p))

0

练习 1.6

题目:Alyssa 并不明白为什么 if 需要提供为一种特殊形式,”为什么不能直接通过 cond 将其定义为一个常规过程呢?“ 她的朋友 Eva 认为确实可以这样做,并定义了 if 的一个新版本:

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

Alyssa 使用 new-if 重写了平方根程序:

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

该程序可以正常运行吗?请解释。

解答

很遗憾,该程序并不能正常运行。因为 new-if 是遵循应用序求值的,无论 good-enough? 的判断结果如何,sqrt-iter 都会被先求值,导致程序无限循环运行下去。

而特殊形式 if 则会先对谓词进行求值,再根据其结果选择其中一个表达式进行求值,从而得出正确的结果。

练习 1.7

题目:对于很小的数的平方根而言,之前使用的 good-enough? 测试的效果不会很好。此外,在实际的计算机中,算术运算往往是以有限精度进行的,这会使我们的测试不适用于非常大的数。请举例说明上述两种情况。另一种实现 good-enough? 的策略是观察猜测值从一次迭代到下一次的变化情况,当改变值相对于猜测值的比率很小的时候就结束,请设计一个采用这种测试方法的平方根过程,并测试其对于很小的数和很大的数的效果。

解答

对于很小的数来说,之前设置的误差阈值 0.001 有些过大了。例如,在我的电脑上计算 (sqrt 0.0001) 时,结果为 0.03230844833048122,与正确答案 0.01 相差较大。

对于很大的数来说,机器的有限精度无法表示两个大数之间的微小差别,这会导致程序无限执行下去(差距永远不会小于 0.001)。例如,在我的电脑上计算 (sqrt 10000000000000) 时,程序会无限执行,guess 始终保持在 3162277.6601683795

使用另一种策略,可以提升对大数和小数的效果,这里不去计算前后两个猜测值的变化率,直接判断两者是否相等:

(define (sqrt x)
  (define (average x y)
    (/ (+ x y) 2))
  (define (square x) (* x x))
  (define (improve guess) (average guess (/ x guess)))
  (define (good-enough? guess)
    (= (improve guess) guess))
  (define (sqrt-iter guess)
    (if (good-enough? guess)
        guess
        (sqrt-iter (improve guess))))
  (sqrt-iter 1.0))

改进后的程序对之前两个数字的测试结果如下:

Input: (sqrt 0.0001)
Output: 0.01

Input: (sqrt 10000000000000)
Output: 3162277.6601683795

练习 1.8

解答

使用上一题中的改进策略,编写过程如下:

(define (root x)
  (define (average3 x y z)
    (/ (+ x y z) 3))
  (define (square x) (* x x))
  (define (improve guess)
    (average3 (/ x (square guess)) guess guess))
  (define (good-enough? guess)
    (= (improve guess) guess))
  (define (root-iter guess)
    (if (good-enough? guess)
        guess
        (root-iter (improve guess))))
  (root-iter 1.0))