2.5.1 通用的算术操作

2.5.1 通用的算术操作

设计通用的算术操作的任务与设计通用的复数操作是相似的。
例如我们想做的是,有一个通用的加法程序,它的行为与普通数的加法 + ,
有理数的加法 add-rat,复数的加法 add-complex 一样。我们能实现加法,其它的操作类似。
通过使用与2.4.3部分中相同的策略,即为复数实现通用的选择子。
为不同的类型的数,我们将加一个类型的标签,使得通用的程序能够
根据参数中的数据类型值,找到合适的程序包。

通用的算术程序定义如下:
(define (add x y) (apply-generic 'add x y))
(define (sub x y) (apply-generic 'sub x y))
(define (mul x y) (apply-generic 'mul x y))
(define (div x y) (apply-generic 'div x y))

我们开始为了处理常规的数,而安装一个程序包。也就是我们的语言的原生的数。
我们标识这些用符号 scheme-number.在这个程序包中的算术操作,都是原生的算术操作程序
(不需要定义额外的程序来处理没有标签的数据)。因为这些操作都有两个参数,它们被安装在表中
被列表(scheme-number,scheme-number)键值化。

(define (install-scheme-number-package)
     (define (tag x)
          (attach-tag 'scheme-number x))
     (put 'add '(scheme-number scheme-number)
           (lambda (x y) (tag (+ x y))))
     (put 'sub '(scheme-number scheme-number)
           (lambda (x y) (tag (- x y))))
     (put 'mul '(scheme-number scheme-number)
           (lambda (x y) (tag (* x y))))
     (put 'div '(scheme-number scheme-number)
           (lambda (x y) (tag (/ x y))))
     (put  'make 'scheme-number
           (lambda (x) (tag x)))
     'done
)

使用scheme-number程序包将创建有标签的普通数。使用如下的程序:
(define (make-scheme-number n)
     ((get 'make 'scheme-number) n))

现在,通用的算术系统的框架是有了,我们能创建包括新类型的数据了。
这有一个执行有理数算术的程序包。 注意,作为可累计性的一个好处,
我们能在程序包中使用以内部程序的方式使用2.1.1部分中的有理数的代码,却不用改一点代码。

(define (install-rational-package)
  ;; internal procedures
   (define (numer x) (car x))
   (define (denom x) (cdr x))
   (define (make-rat n d)
        (let ((g (gcd n d)))
            (cons (/ n g) (/ d g))))
  (define (add-rat x y)
   (make-rat (+ (* (numer x) (denom y))
                (* (numer y) (denom x)))
             (* (denom x) (denom y))))

  (define (sub-rat x y)
     (make-rat (- (* (numer x) (denom y))
                (* (numer y) (denom x)))
             (* (denom x) (denom y))))

  (define (mul-rat x y)
     (make-rat  (* (numer x) (numer y))      
             (* (denom x) (denom y))))

  (define (div-rat x y)
     (make-rat (* (numer x) (denom y))      
             (* (denom x) (numer y))))

   ;; interface to rest of the system
   (define (tag x)
          (attach-tag 'rational x))
     (put 'add '(rational rational)
           (lambda (x y) (tag (add-rat x y))))
     (put 'sub '(rational rational)
           (lambda (x y) (tag (sub-rat x y))))
     (put 'mul '(rational rational)
           (lambda (x y) (tag (mul-rat x y))))
     (put 'div '(rational rational)
           (lambda (x y) (tag (div-rat x y))))
     (put  'make 'rational
           (lambda (n d) (tag (make-rat n d))))
     'done
)

(define (make-rational n d)
     ((get 'make 'rational) n d))

我们能够安装一个相似的软件包,来处理复数的计算,使用标签complex.
在增加的软件包中,我们能从表中抽取出操作 make-from-real-imag和make-from-mag-ang.
这些操作是被平面坐标和极坐标的软件包定义的。累加性允许我们使用它们作为内部程序,
相同的还有2.4.1部分中的add-complex,sub-complex,mul-complex,div-complex.

(define (install-complex-package)
  ;; internal procedures from rectangular and polar packages
   (define (make-from-real-imag x y) ((get 'make-from-real-imag 'rectangular) x y))

   (define (make-from-mag-ang r a)
      ((get 'make-from-mag-ang 'polar) r a))
  ;; internal procedures
   
 (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))))
 
;; interface to rest of the system
   (define (tag x)
          (attach-tag 'complex x))
     (put 'add '(complex complex)
           (lambda (x y) (tag (add-complex x y))))
     (put 'sub '(complex complex)
           (lambda (x y) (tag (sub-complex x y))))
     (put 'mul '(complex complex)
           (lambda (x y) (tag (mul-complex x y))))
     (put 'div '(complex complex)
           (lambda (x y) (tag (div-complex x y))))
     (put  'make-from-real-imag 'complex
           (lambda (n d) (tag (make-from-real-imag x y))))
     (put  'make-from-mag-ang 'complex
           (lambda (r a) (tag (make-from-mag-ang r a))))
     'done
)

复数软件包外部的程序能组装复数从实部和虚部,也可以从长度和角度。注意在
平面坐标软件包和极坐标软件包中的原来定义的候选程序如何被导出到复数软件包,
如何从那被导出到外部的世界中。

(define (make-complex-from-real-imag x y)
((get 'make-from-real-imag 'complex) x y))
(define (make-complex-from-mag-ang r a)
((get 'make-from-mag-ang 'complex) r a))

这里我们有的内容是一个两层的标签系统。一个典型的复数,例如在平面坐标中的3+4i
能被表示成图2.24.外层标签被用来把数指向复数软件包。一旦到了复数软件包,
下一层标签被用来把数指向平面坐标的软件包。在一个大型的复杂的系统中可能有许多层,
通过使用通用化操作的方法,任何一个接口都指向了下一层。正如一个数据对象是向下通过,
被用来把它指向合适的软件包的外层标签被脱掉了(通过应用contents) 并且下一层的标签
(如果有的话)它是用来进一步的分发的,它才变成可见的状态。
           ___ ___            ___ ___            ___ ___
_________>| * | * |_________>[ * | * ]_________>[ * | * ]
            |                   |                 |   |
     \/                 \/                \/   \/
          [复数]               [平面坐标]        [3] [4]

图2.24 平面坐标中的3+4i的表示

在上述的软件包中,我们使用add-rat,add-complex,和其它的算术程序正如原来的写法。
一旦这些定义是不同的安装程序的内部定义,然而,它们不再需要名称间彼此保持唯一。
在各个软件包中,我们都能简单地命名它们为add,sub,mul,div.

练习2.77
Louis Reasoner尝试解释表达式(magnitude z).z如图2。24中的对象。
令他吃惊的是,代替答案5的是他得到了一个从apply-generic程序中报错消息。
说对于复数的类型,长度的操作没有这个方法。他把这个交互消息给了Alyssa。
她说问题是复数的选择子没有针对复数的定义,只有在极坐标和平面坐标的数中有定义。
为了让它工作,你必须要做的工作是把如下的程序加到复数的软件包中:

(put 'real-part '(complex) real-part)
(put 'imag-part '(complex) imag-part)
(put 'magnitude '(complex) magnitude)
(put 'angle '(complex) angle)

详细地描述一下这为什么是有效的。作为一个例子,跟踪一下在解释表达式
(magnitude z)调用的所有的程序。z如图2。24中的对象。特别是,
程序apply-generic被调用了多少次?在任何一种情况下,被分发的程序是什么?

练习2.78
在scheme-number软件包中的内部程序仅有对原生程序的调用,例如加减法。
直接使用语言的原生的程序是不可能的,因为我们的类型标签系统要求任何一种数据
对象都要带有一个标签。事实上,然而,所有的LISP实现都带有一个类型系统。
它们在内部使用。原生的判断式例如symbol? 和number?都检查数据对象是否有特定的
类型。为了利用语言的内部的类型系统,修改2.4.2部分中的程序 type-tag,contents,
和attach-tag。也就是说,系统应该工作如前,只是除了普通数应该被表示为语言的普
通数这一点。而不是把普通数作为一个数对,这个数对的头部是符号scheme-number.


练习2.79
定义一个通用的相等判断式equ?,用来测试两个数是否相等,把它
安装到通用算术软件包中。这个操作应该能对普通数,有理数,和
复数都有效。

练习2.80
定义一个通用的判断式=zero?,测试它的参数是否是0,把它
安装到通用算术软件包中。这个操作应该能对普通数,有理数,和
复数都有效。

猜你喜欢

转载自blog.csdn.net/gggwfn1982/article/details/81503668