在第二章开始就利用有理数的运算来说明数据抽象带来的好处,数据的操作与具体实现相分离,能很好的控制程序的复杂性。例如通过构建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等构造选择函数根据这个类型标志,从不同的系统中选择合适的处理函数,获得实部和虚部。