Program Structure 笔记9(B) (数据抽象、序列数据的表示)
(作者:colinboy Email:cybbh@163.com) 2008.5.20
(内容难免出现错误或一些专业词汇使用不当,只是个人笔记,能理解总体内容就好)
Pairs(对,对序)
各种计算机语言都提供了一些方法把一些基本数据组合起来,形成另一种数据的能力(例如struct,class等). 在scheme中,有一种内置的数据组合的方法,组合出来的数据称为对(Pair).
一个对的结构如下:
一个对中有2个元素组成,上面的数据我们可以给它一个定义,如它表示5分之3.那么这个对就成为了一个ADT,它表示一个分数.
"对"是一个ADT,需要有选择函数和构造函数.
对的构造函数为cons,是constructor的简写.
对的选择函数为car和cdr,car用来选择左边的元素,cdr用来选择右边的元素.
(define fraction (cons 5 6)) --> (5 . 6)
(car fraction) --> 5
(cdr fraction) --> 6
对于如下组织的对,构造方法为:
(define p (cons (cons 'a 'b) (cons (cons 'c 'd) 'e)))
如果我们要选择元素c,方法为:
(car (car (cdr p))) --> c
scheme是很聪明的语言,为了获取元素c,我们也可以写成(caadr p),根据a和d的个数来生成car和cdr, caadr表示去掉前面的c和最后的r,还剩下aad,a表示调用car,d表示调用cdr,所以aad表示(car (car (cdr p))), (cdadr p)选择了元素d,和上面一样,去掉c和r,还剩dad,表示(cdr (car (cdr p))).
由此可见,一个对中包含的元素可以还是一个对,这种能力称为闭包.一个数据是闭包的,就是说通过它组合起来的数据结果还可以通过同样的方法再进行组合.
"对"给我们提供了把2个数据组织起来的能力,但是如果想组织超过2个元素的数据怎么办?为此需要另一种ADT,在scheme中提供的这种把n个数据组织起来的ADT成为序列或表(sequence or list).
一个序列需要n个对来表示,序列是基于"对"构建的.
序列的逻辑结构如下:
序列ADT结构图
|------------------------|
| sequence list |
|------------------------|
| cons car cdr list |
| append list-ref |
|------------------------|
| 内部实现pairs |
|------------------------|
要构建一个序列,可以使用cons或者list或者append.
使用cons构建序列:
(cons 'a (cons 'b (cons 'c (cons 'd (cons 'e (cons 'f '())))))) -> (a b c d e f)
此语句的返回值为(a b c d e f),这是序列的表示方法,看起来和sentence好像没有太大区别.
我们输入(cons 'a 'b),会返回(a . b),由此推理上面的一串cons语句应该返回:
(a . (b . (c . (d . (e . (f . '()))))))
但是显然没有这样,scheme执行上面的一串cons一句,它知道我们创建的是一个序列,然后会自动返回一个序列,而序列的表示方法为(a b c ...).
使用list构建序列:
(list 'a 'b 'c 'd 'e 'f) --> (a b c d e f)
此语句和上面的一串嵌套调用的cons完成同样的功能,其实在scheme中,list语句会自动转换成递归调用cons.
还可以使用list用如下方式构建序列
(list '(a b) '(c d)) --> ((a b) (c d))
返回结果为((a b) (c d)),它表示此序列有2个元素,并且每个元素又是一个含有2个元素的序列.
(cons '(a b) '(c d)) --> ((a b) c d)
怎么回事? (cons '(a b) '(c d))为什么不返回((a b) (c d)),而是返回了((a b) c d)?
(a b) 和(c d)都是一个表, 显然(cons '(a b) '(c d))被解释成把表(a b)当成一个数据粘到表(c d)的前面.输出结果就为((a b) c d)了.
换句话说就是我们本来有一个含有2个数据的序列(c d),现在要把它和一个数据a粘接起来,粘接后结果为(a c d),假设a为(a b),那么结果就为((a b) c d)啦~~~
用append创建序列:
(append '(a b) '(c d)) --> (a b c d)
(append '(a b) '(c d) '(e f)) --> (a b c d e f)
注意哦, '(a b)表示一个序列(a b),恩... 太像sentence了,根本就看着一摸一样.
append需要序列作为参数,把输入的序列组合成一个新的更大的序列.
例如
(append 'a '(b c)) --> 错误,因为'a不是一个序列.
但是
(append '(b c) 'd) --> (b c . d)
又发生了什么? 不是说append都要序列作为参数么? 'd不是序列哦.
其实可以理解成(append '(b c) 'd)想把'd追加到序列(b c)后面,这样就要求(b c)中要有空闲的位置能存储这个"链"结构,显然存储c的节点的"对"的右边是空闲的,然后就把'd放进这个空闲位置中了.
然而在(append 'a '(b c))中,想把序列(b c)加到'a后面,但是'a就表示字符a,根本没有什么空闲位置能存储这个"链".