这里带入了“数据导向的程序设计方式”,这种方式适合于处理某种数据(这里是复数)有多种表达方式,而带来的问题。
前面的2.4.2中采用的是基于类型的显示分派,它采用一集过程作为复数算数与两个表示包之间的界面,这种发式有两个弱点:1,每个过程都必须知道复数的所有表达方式;2,命名困难。两弱点的根源是这种基于类型的显示分配不具有“可加性”。
数据导向的程序设计,把所有的针对复数的操作,按照不同的类型和操作,将其输入表格,形成一个二维表格(如果复数不止两种表达方式,就不止二维表格)。而数据导向的程序设计就是使程序能直接利用这种表格工作的程序设计方式。
在该节中的计算复数的二维表格就是下图,它的第一维是operation,第二维是数据类型.按照(get 'operation 'polar)。
我想要(define z1 (make-from-real-imag 1 2)),
程序运行不了,显示expected procedure, given: #f
检查原因:
1,表格正常运行
2,原因在于没有安装程序包,安装: (install-rectangular-package) (install-polar-package)
OK
关键过程:
;apply-generic---------------key
(define (apply-generic op . args)
(let ((type-tags (map type-tag args))) ;args is a complex,z
(let ((proc (get op type-tags))) ;proc is the needed process
(if proc
(apply proc (map contents args)) ;the second param of apply must be a list
(error "No method for these types -- APPLY-GENERIC"
(list op type-tags))))))
在数据导向的程序设计中,最关键的想法是通过显示处理--类型表格(如上图)的方式,管理程序中的各种通用型操作(如real-part,imag-part)。
而2.4.2节中的是一种基于类型(polar,rectangular)进行分派的组织方式,基于类型进行分派就是让每个操作(即通用型过程如real-part,imag-part...)管理自己的分派。
(define (real-part z)
(cond ((rectangular? z)
(real-part-rectangular (contents z)))
((polar? z)
(real-part-polar (contents z)))
(else (error "Unknown type -- REAL-PART" z))))
从效果上来讲,这种方式就是将上表分成一行一行,每个通用型过程表示表格中的一行。
另一种实现策略是将这一表格按列进行分解,不是采用一批“智能操作”(即上面的通用型过程)去基于数据类型进行分派,而是采用“智能数据对象”,让它们基于操作名完成所需要的分派工作。而所谓的“智能数据对象”也是由过程来实现,就是dispatch方式。
(define (make-from-real-imag x y)
(define (dispatch op)
(cond ((eq? op 'real-part) x)
((eq? op 'imag-part) y)
((eq? op 'magnitude)
(sqrt (+ (square x) (square y))))
((eq? op 'angle) (atan y x))
(else
(error "Unknown op -- MAKE-FROM-REAL-IMAG" op))))
dispatch)
这里,make-from-real-imag返回的是一个过程--内部的dispatch过程。这种风格称为消息传递,名字来源于,将数据对象设想成一个实体,而它以消息的方式接收到所需操作的名字。
注意在本节中利用表格进行计算时采用的是并不是消息导向的程序设计,而是数据导向的程序设计。