SICP学习笔记(1.3.4)

                                           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的观点,并随着理解的深入其中的内容可能会被修改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值