SICP学习笔记(1.3.4)
周银辉
1,过程作为返回值
在1.3中我们明白了高阶函数之后,“用一个过程作为另外一个过程的返回值”则是稀松平常的事情了,比如下面的代码:
(define (f x)
(+ x 1))
(define (g) f)
((g) 2)
函数g没有参数,其返回值为函数f,所以((g) 2)就运算结果就是(f 2),最后运算结果为3。
上面是用一个已命名的函数作为返回结果的,相应的,也可以将一个“匿名过程”作为结果返回,这里的“匿名过程”也就是我们的Lambda表达式,所以上面的代码可以改造成:
(define (g)
(lambda (x) (+ x 1)))
((g) 2)
那么((g) 2)的运算结果就是((lambda (x) (+ x 1)) 2),最后运算结果为3。
2,牛顿法
学到这里,你可能需要复习一下高等数学的基本内容,包括“导数”和“微分”,高数的在线教材可以在这里找到:http://sxyd.sdut.edu.cn/gaoshu1/index.htm
关于牛顿法的介绍可以看这里:http://en.wikipedia.org/wiki/Newton%27s_method ,下面是程序:
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) 0.000000001))
;定义不动点函数
(define (fixed-point f first-guess)
(define (try guess step-count)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next (+ step-count 1)))))
(try first-guess 0))
;定义导数函数
(define (D f)
(lambda (x dx) (/ (- (f (+ x dx)) (f x)) dx)))
;牛顿法
(define (newton g first-guess)
(fixed-point (lambda (x) (- x (/ (g x) ((D g) x 0.000000001)))) first-guess))
;平方
(define (square x)
(* x x))
;定义开方,来测试下牛顿法
(define (sq x)
(newton (lambda (y) (- (square y) x)) 1.0))
(sq 5)
3,“一等公民”
这里列出了程序语言中作为“一等公民”的语言元素所具备的几个“特权”:
- 可以用变量命名
- 可以作为过程参数
- 可以作为过程返回结果
- 可以被包含在数据结构中
4,练习1.40
求三次方程 x^3 + ax^2 + bx + c 的零点。
首先,证明 函数f(x) = x^3 + ax^2 + bx + c 是“可微”的:
由可导和可微的性质知道,可导和可微互为充要条件,所以,要证可微我们可以先证可导,
f ’ (x) = (x^3)’ + (ax^2)’ + (bx)’ + (c)’
= 3x^2 + 2ax + b
所以f(x)的导数存在,那么f(x)可导,其必定可微。
其次,利用“牛顿法”:如果f(x)是可微函数,那么f(x)=0的一个解就是函数(x – f(x)/df(x)的一个不动点,其中df(x)是f(x)的导数。所以我们可以轻松得到下面的代码:
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) 0.000000001))
;定义不动点函数
(define (fixed-point f first-guess)
(define (try guess step-count)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next (+ step-count 1)))))
(try first-guess 0))
;定义导数函数
(define (D f)
(lambda (x dx) (/ (- (f (+ x dx)) (f x)) dx)))
;牛顿法
(define (newton g first-guess)
(fixed-point (lambda (x) (- x (/ (g x) ((D g) x 0.000000001)))) first-guess))
;定义cubic函数,也就是我们题目中所谓的f(x)
(define (cubic a b c)
(lambda (x) (+ (* x x x) (* a x x) (* b x) c)))
;随便定义几个系数
(define a 3)
(define b 5)
(define c 8)
(define result (newton (cubic a b c) 1.0))
;定义一个验证过程,让其验证得到的解,是否让方程成立
(define (validate x)
(= 0 (+ (* x x x) (* a x x) (* b x) c)))
;输出结果
result
;验证结果
(validate result)
比如上面我们计算 x^3 + 3x^2 + 5x + 8 = 0, 其一个解为:-2.3282688556686084
5,练习1.41
double 将函数f计算两次,那么 (double double)将计算4次,(double (double double))将计算14次,所以返回值应该是21,可以运行下面的代码试试 :
(define (double f)
(lambda (x) (f (f x))))
(define (inc a) (+ a 1))
(((double (double double)) inc) 5)
6,练习1.42
关于复合函数,非常简单,直接贴答案了:
(define (inc a) (+ a 1))
(define (square a) (* a a))
(define (compose f g)
(lambda (x) (f (g x))))
((compose square inc) 6)
7,练习1.43
f(f(f…(f(x)…))实际上就是函数 f 被复合N次:
(define (inc a) (+ a 1))
(define (square a) (* a a))
(define (compose f g)
(lambda (x) (f (g x))))
(define (repeated f count)
(define (repeated-inner f step)
;如果复合到了指定的次数,则返回被复合后的函数
(if (= step count)
f
;否则,再被复合一次
(repeated-inner (compose f f) (+ step 1))))
(lambda (x) ((repeated-inner f 1) x)))
((repeated square 2) 5)
8,练习1.44
;定义复合函数
(define (compose f g)
(lambda (x) (f (g x))))
;定义N次复合函数
(define (repeated f count)
(define (repeated-inner f step)
(if (= step count)
f
(repeated-inner (compose f f) (+ step 1))))
(lambda (x) ((repeated-inner f 1) x)))
;定义平滑函数
(define (smooth f)
(lambda (x) (/
(+ (f (- x 0.0001)) (f x) (f (+ x 0.0001)))
3)))
;定义N次平滑函数
(define (smooth-n f count)
(repeated (smooth f) count))
9,练习1.45
基本上全部利用以前的劳动成果:不动点、平均阻尼、复合函数等,来球N次方根
(define (close-enough? v1 v2)
(< (abs (- v1 v2)) 1.0E-20))
;定义不动点函数
(define (fixed-point f first-guess)
(define (try guess step-count)
(let ((next (f guess)))
(if (close-enough? guess next)
next
(try next (+ step-count 1)))))
(try first-guess 0))
;定义复合函数
(define (compose f g)
(lambda (x) (f (g x))))
;定义N次复合函数
(define (repeated f count)
(define (repeated-inner f step)
(if (= step count)
f
(repeated-inner (compose f f) (+ step 1))))
(lambda (x) ((repeated-inner f 1) x)))
;定义x的n次方
(define (power x n)
(define (power-inner step result)
(if (= step n)
result
(power-inner (+ step 1) (* result x))))
(power-inner 0 1))
;定义平均阻尼函数
(define (average v1 v2)
(/ (+ v1 v2) 2.0))
;平均阻尼的Currying版本
(define (avg v1)
(lambda (v2) (/ (+ v1 v2) 2.0)))
;定义N次平均阻尼函数
(define (average-n v1 v2 n)
((repeated (avg v1) n) v2))
;定义平方根
(define (root x)
(fixed-point (lambda (y) (average y (/ x y))) 1.0))
;定义N次方根
(define (root-n x n)
(fixed-point (lambda (y) (average-n y (/ x (power y (- n 1))) n)) 1.0))
;测试,计算2的平方根
(root 2)
;测试,计算12的3次方根
(root-n 12 3)
值得注意的地方是定义N次平均阻尼函数,由于我们定义的(Repeated f count)时默认了函数f只带一个参数,所以不能直接将带有两个参数的average函数拿去做复合运算,所以我们需要将其Currying化为一个参数,也就是我们的avg版本。另外,上面在使用average-n时我将其平均阻尼次数设置为了n(也就是方根的次数),至于刚好多少次就可以收敛,我没有去试验。
10,练习1.46
直接一个尾递归将题目的意思翻译成代码就可以了:
(define (interative-improve isGood improveIt)
(lambda (x)
;如果值足够好,则返回值
( if (isGood x)
x
;否则,将修正后的值(improveIt x)拿去继续计算
((interative-improve isGood improveIt) (improveIt x)))))
;以下几个函数为测试函数
(define (good-enough? x)
(< x 1.0E-10))
(define (improve x)
(* x 0.1))
((interative-improve good-enough? improve) 5.0)
注:这是一篇读书笔记,所以其中的内容仅属个人理解而不代表SICP的观点,并随着理解的深入其中的内容可能会被修改