Program Structure 笔记9(A) (数据抽象、序列数据的表示)
(作者:colinboy Email:cybbh@163.com) 2008.5.19
(内容难免出现错误或一些专业词汇使用不当,只是个人笔记,能理解总体内容就好)
本节内容:
数据抽象 对(pairs) 表(list)
一个数据抽象的例子
(define (total-hand hand)
(if (empty? hand)
0
(+ (butlast (last hand))
(total-hand (butlast hand)))))
(total-hand '(3h 10c 4d)) --> 17
上面过程如果不仔细分析,也许不能很快看出来它的作用是什么。它的作用是接收一组牌,然后计算出这组牌的大小的总和。显然此过程处理的是“牌”,但是在此过程中,我们并看不到任何关于“牌”的影子。
为了更容易理解上面的程序,定义出了此程序的第二版:
(define card-rank butlast)
(define card-suit last)
(define one-card last)
(define remaining-cards butlast)
(define (total-hand hand)
(if (empty? hand)
0
(+ (card-rank (one-card hand))
(total-hand (remaining-cards hand)))))
(total-hand '(3h 10c 4d)) --> 17
第二版中终于看到了“牌”的影子,它的功能和第一版完全一样,但是比第一版更容易阅读了,并且让人感觉到真的有一些“牌”存在了。
第二版相比第一版也更容易维护,例如如果我们的程序中有几百个函数都需要获取牌的大小,第一版中我们需要在这几百个函数中使用(butlast card)来获取牌的大小,设想如果现在我们想把牌的表示形式从“10c”修改称“c10”,那么我们就需要修改几百个函数来实现,但是在第二版中,我们有一个card-rank来获取牌的大小,如果牌的表示形式改变了,只需要修改card-rank就可以完成。
在本例中显然card应该设计成一种ADT(抽象数据类型),为了实现card,需要:
1. 选择函数(selector)
上面定义的card-rank card-suit都是选择函数,用来选择数据中的某部分。
2. 构造函数(Constructor)
用来构造一个抽象数据存储,例如card。
card的构造函数实现如下:
(define (make-card rank suit)
(word rank (first suit)))
(make-card 10 'club) --> 10c
(make-card 3 'spade) --> 3s
现在我们来实现一个更好的total-hand,第三版:
(define card-rank butlast)
(define card-suit last)
(define one-card last)
(define remaining-cards butlast)
(define make-hand se)
(define (make-card rank suit)
(word rank (first suit)))
(define (total-hand hand)
(if (empty? hand)
0
(+ (card-rank (one-card hand))
(total-hand (remaining-cards hand)))))
(total-hand (make-hand (make-card 3 'heart)
(make-card 10 'club)
(make-card 4 'diamond))) --> 17
第三版和第二版相比,发现除了增加了make-hand和make-card外,其他地方和第二版没有任何区别!
这就是数据抽象,当我们定义出选择函数和构造函数之后,如果数据的表示形式发生了改变,只需要改变选择函数和构造函数,程序其他地方几乎不用修改,使程序的可读性和可维护性更好!
例如要更改card的表示形式,不使用以前的'(10s 5h 8c), 而修改成如'(3 34 41),即根据牌的花色
顺序依次把牌表示成一个固定的数字,例如红心A表示成1,黑心A表示成14,方块A表示成27,可只把选择函数和构造函数修改成如下形式,而程序的其他部分完全不需要修改。
(define (make-card rank suit)
(cond ((equal? suit 'heart) rank)
((equal? suit 'spade) (+ 13 rank))
((equal? suit 'diamond) (+ 26 rank))
((equal? suit 'club) (+ 39 rank))
(else (error "what???"))))
(define (card-rank card)
(remainder card 13))
(define (card-suit card)
(nth (/ card 13) '(heart spade diamond club)))
一个Card ADT的层次结构如下:
|------------------------|
| Card |
|------------------------|
| 选择函数 构造函数 |
|------------------------|
| 内部实现 |
|------------------------|
最高层为抽象数据类型的名字,中间曾为展示给用户的使用接口,最底层为抽象数据的实现。
用户使用Card时只需要和中间层的用户接口交互,而不需要关心内部实现。