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

在第二章开始就利用有理数的运算来说明数据抽象带来的好处,数据的操作与具体实现相分离,能很好的控制程序的复杂性。例如通过构建make-rat、numer,denom三个过程,使得有理数的使用方式和具体表示相分离;然后,在此基础上构建有理数的基本运算过程add-rat、sub-rat、mul-rat和div-rat。这四个基本运用是利用make-rat、numer、denom三个接口完成的。也就是说,如果你修改了这三个过程的实现,只要保证它们的值符合有理数表示的概念,构造在此基础上的四个运算过程以及使用这些运行的程序都不需要修改,程序依然能正确运行。

但是如果有理数的表示有多种方式,需要一个统一的运算系统,利用上述三个选择构造函数来定义四个基本运算过程就行不通了。必须引入更多的抽象,屏蔽实现方式的不同。

有理数的表示方式有以下两种:直角坐标系表示法(x . y),x表示实坐标,y表示虚坐标;极坐标表示法(r . a),r表示有理数的模,a表示幅角。无论是哪种方式表示,都可以定义以下选择函数和构造函数,构建抽象屏蔽:real-part、imag-part、magnitude、angle、make-from-real-imag和make-from-mag-ang。而有理数的运算就是基于上面的抽象屏蔽定义的。

(define (add-complex z1 z2)
    (make-from-real-imag (+ (real-part z1) (real-part z2))
                         (+ (imag-part z1) (imag-part z2))))
(define (sub-complex z1 z2)
    (make-from-real-imag (- (real-part z1) (real-part z2))
                         (- (imag-part z1) (imag-part z2))))
(define (mul-complex z1 z2)
    (make-from-mag-ang (* (magnitude z1) (magnitude z2))
                       (+ (angle z1) (angle z2))))
(define (div-complex z1 z2)
    (make-from-mag-ang (/ (magnitude z1) (magnitude z2))
                       (- (angle z1) (angle z2))))
而如果是直角坐标系表示有理数,也就是z1、z2是有(x . y)表示的,x、y分别表示实坐标和虚坐标。那么选择函数和构造函数的定义如下:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (real-part z) (car z))
(define (imag-part z) (cdr z))
(define (magnitude z)
    (sqrt (+ (square (real-part z)) (square (imag-part z)))))
(define (angle z)
    (atan (imag-part z) (real-part z)))
(define (make-from-real-imag x y) (cons x y))
(define (make-from-mag-ang r a)
    (cons (* r (cos a)) (* r (sin a))))
有两个构造函数,分别表示接受直角坐标系参数和极坐标参数时,如何构造有理数,返回的有理数都是用直角坐标系表示。

在极坐标表示的情况下,同样定义上述基本过程。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (real-part z) 
      (* (magnitude z) (cos (angle z))))
(define (imag-part z) 
       (* (magnitude z) (sin (angle z))))
(define (magnitude z) (car z)) 
(define (angle z) (cdr z))    
(define (make-from-real-imag x y) 
     (cons (sqrt (+ (square x) (square y)))
           (atan y x)))
(define (make-from-mag-ang r a) (cons r a)) 

这样的数据抽象保证了add-complex、sub-complex、mul-complex和div-complex在同一套实现下都能正常工作。

但是还不是很方便,因为这个两个系统不能共存。在给出(3 . 4)之后,系统并不能自动的识别当前在哪个坐标表示之下,因此不能选择合适的real-part、magnitude等操作,进而add-complex等过程也不能保证正确。要达到通用的目的,数据结构里必须包含类型标志,这个标志指示所在的数据结构是表示哪种坐标方式。real-part、imag-part等构造选择函数根据这个类型标志,从不同的系统中选择合适的处理函数,获得实部和虚部。





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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、付费专栏及课程。

余额充值