第一章学习的是程式抽象:将procedures组合抽象,产生功能强大的复杂procedure; 这一章要学习的是数据抽象:将数据组合抽象,产生功能强大的复杂数据;
和复杂程式抽象一样,复杂数据(compound data)也是编程语言reuse的产物,还是作者提炼的好:
- 概念上的提升
- 模块化
- 表现力
编程语言要在这几方面上做好,不仅需要我们之前学到的函数式的抽象,还需要函数式所操作的各类数据的抽象。
以有理数 y = 0.4 为例,可以表示为 y = 2/5, 即 y由两个数组合而成,那我们写程序的时候,是以哪种y的表示方式呢? 比如可以:
def func(y):
return 2 * y
func(0.4)
也可以:
def func(n, m):
return 2 * n / m
func(2, 5)
你用哪种? 自然是前者对吧,简单明了。后者在空间上还要占用两个位置呢,而且实际应用中可能还要保存n和m的关系。 y=0.4作为一个概念单位,很清楚,有理数。 y=2/5你想表达什么?
数据抽象说白了就是,将程序中的这两部分分离开:
- 定义数据对象
- 使用数据对象
The general technique of isolating the parts of a program that deal with how data objects are represented from the parts of a program that deal with how data objects are used is a powerful design methodology called data abstraction.
比如前面的有理数的例子:
有理数y是如何定义的? y = 2/5, 是两个数想出而得
有理数y是如何使用?作为函数func的参数,在函数调用时代入,直接代入y=0.4就可以了,无需将其定义(y=2/5)中的2和5也分别代入。
作者书中还举了线性组合ax+by的例子,在a,b, x, y都是数字的时候可以这样:
def func(a, x, b, y):
return a * x + b * y
这里用到的是 * 和 + 原始表达式
但考虑到实际应用中可能的各类复杂事物用a b x y表达的话,作者说可以这样写:
def func(a, x, b, y):
return add(mul(a, x), mul(b, y))
(插一句,其实python这样写的话括号也不亚于lisp啊)
看到么,作者另外定义了add 和 mul函数, 替代了加和乘原始表达式。为什么?因为非数字型的不能用加和乘啊。
但是,线性组合ax+by难道不是都处理的数字么?我数学全还给老师了。。。目前不解。
但是,作者的思路还是可以理解一些,比如我这么想的:
如果要你定义一个函数,参数是 a 和 x, b, y,a,b 可以是任意数据类型,x,y是数字:
- 当a是数字时,返回a和x的乘积
- 当b是数字时,返回b和y的乘积
- 当a是字符时,返回x个a组成的字符
- 当b是字符时,返回y个b组成的字符
因为数字和字符类型不同,我们可以定义两个子函数,分别处理a和b是数字与字符,比如:
def mul(a, x):
return a * x
def conca(a, x):
res = ""
for i in range(x):
res += a
return res
是不是挺傻的?
其实如果你在ipython中输入 ```'d' * 5 `` 会直接得到 ddddd,因为python重载了乘法运算符。
对于前面的需求,完全可以只用mul()一个函数即可。
但是如果数据既不是数字,也不是字符呢?就继续扩展mul()呗。
写到这里,感觉我还是没有表达清楚作者的思路。其实和函数抽象一样,数据抽象也是将数据的具体操作黑盒化了,比如你用到的是mul(a, x),当你传入不同的a,x时,你无需关心a和x是如何被处理的,只要关心最后的结果是不是你想要的。似乎还是没能说清楚,这mul的抽象不也是这样么,不用关心内部怎么实现的。如何区分数据处理和过程处理(process总要用到数据的)的抽象?
- 简单如第一章中所看到的,数字
- 复杂如数据库中的数据、类的实例等
- 普通如字符串、布尔值等
- blah...
- 讨论复杂数据和数据抽象
- 数据是如何“glue”(粘)到一起组成复杂数据的。作者提到了closure闭包,正好我还没来得及单独学闭包。
- 符号表达式,介绍各种数据对象,比如集合、编码等
- 更高层次的数据抽象,面向数据编程