题目:下面是一系列表达式,对于每个表达式,解释器将输出什么结果?假定这一系列表达式是按照给出的顺序逐个求值的。
(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=3
,b=4
,之后的表达式逐个求值即可。
19
#f
4
16
6
16
(/ (+ 5
4
(- 2 (- 3 (+ 6 (/ 4 5)))))
(* 3
(- 6 2)
(- 2 7))
题目:定义一个过程,以三个数字为参数,返回其中两个较大数字的平方和。
分三种情况分析,找出最小的数,剩下两个数求平方和即可。
(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
并不是必要的,只是为了方便。
题目:我们的求值模型允许组合式的操作符是复合表达式,基于这一思想对下列过程的行为进行描述。
(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
题目: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
题目: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
则会先对谓词进行求值,再根据其结果选择其中一个表达式进行求值,从而得出正确的结果。
题目:对于很小的数的平方根而言,之前使用的 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
使用上一题中的改进策略,编写过程如下:
(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))