《计算机程序的构造与解释》(七)

以list作为操作单元

上一篇介绍如何构建list抽象,然后遍历list进行一些计算,如length等。那么有没有一个抽象,使得过程是以表或者表结构为操作对象,如(square_list ls)返回的是ls表的元素的平方构成的表?

(define (square_list ls)
    (cond ((null? ls) null)
          (else (cons (* (car ls) (car ls))
                      (square_list (cdr ls))))))

square_list是以ls为参数,计算每个元素的平方,然后再构成list并返回。如果需要对list作其他操作,比如开方、加一等呢?通过定义map过程能方便的进行类似的操作,如(map proc ls)。这里的proc可以是任意的包含一个元素参数的过程,这个map过程返回的是proc作用在ls元素中的结果所构成的list。

定义map抽象过程

(define (map proc ls)
   (cond ((null? ls) null)
            (else (cons (proc (car ls))
                            (map proc (cdr ls))))))
再一个例子,定义一个过程实现两个list合并。
(define (append ls1 ls2)
   (cond ((null? ls1) ls2)
         (else (cons (car ls1) (append (cdr ls1) ls2)))))
在append的基础上,定义reverse过程,使得输入的list翻转。如(reverse '(1 2 3 4))得到'(4 3 2 1)。

(define (revers ls)
    (cond ((null?  ls) null)
          (else (append (revers (cdr ls)) (list (car ls))))))

尾递归实现reverse过程

还可以通过定义辅助过程,通过尾递归的方式将list中的元素加入到累加器中。这是尾递归的典型应用。

 (define (revers2 ls)
    (define (revers_aux ls acc)
        (cond ((null? ls) acc)
              (else (revers_aux (cdr ls) (cons (car ls) acc)))))
    (revers_aux ls null))

再改进reverse过程,实现deep-reverse。

;
(define (deep_revers ls)
    (define (revers_aux ls acc)
        (cond ((null? ls) acc)
              ((not (pair? ls)) (cons ls acc))
              (else (revers_aux (cdr ls) (revers_aux (car ls)  acc)))))
    (revers_aux ls null))
(deep_revers '(1 2 (3 4) ( 5 6 7)))
;-> '(7 6 5 4 3 2 1)

定义tree-map

与之前的map过程不同,tree-map能遍历整颗树,而不仅仅是list。然后将proc作用在每个元素上,并返回原来的树结构,只是新元素代替了旧元素。

(define (tree-map proc ls)
    (cond ((null? ls) null)
          ((not (pair? ls)) (proc  ls))
          (else (cons (tree-map proc (car ls))
                      (tree-map proc (cdr ls))))))
> (tree-map inc '(1 2 (3 4)))
;->'(2 3 (4 5))
>(tree-map square '(1 2 (3 4))
;->'(1 4 (9 16))






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 .....
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值