《计算机程序的构造与解释》(八)

来源:互联网 发布:mysql jdbc maven 编辑:程序博客网 时间:2024/05/16 16:01

在《SICP》第二章开始介绍数据抽象时,抽象出有理数的构造和选择函数,然后在这个接口上再定义有理数的基本操作过程,实现了有理数的具体表示和使用的分离。然后又介绍了序对和表结构,以及把表和树作为操作单元的一些过程,有map、tree-map、append、reverse等等。

现在要求一棵树的所有奇数叶子的平方和,按照之前的方法是遍历树,然后判断每个元素是否奇数,如果是则累加起来。

(define (sum-odd-squares tree)    (cond ((null? tree) 0)          ((not (pair? tree))           (if (odd? tree) (square tree) 0))          (else (+ (sum-odd-squares (car tree))                   (sum-odd-squares (cdr tree))))))(sum-odd-squares '(1 (2 3) (4 5 6)));->35
从上面的代码可以看出,在判断tree是否为原子时(not paire?),对树的元素进行了过滤,如果是odd?,那么就求平方;如果tree是一个表结构,那么递归求这个表结构的car和cdr两部分。实际上,如果改变策略,进行下面的抽象会看出一类数据抽象模式。

上面的例子需要:

  1. 枚举出一棵树的叶子节点;
  2. 判断是否为奇数,过滤掉非奇数的节点数值;
  3. 对过滤后得到的所有数值进行平方运算;
  4. 用+累积求平方的结果,从0开始。

可以把需要处理的数据当作数据流看待,就得到下面的框图:


在上面的代码中,这些步骤是相互混合的,没有清晰的层次。如果能设计出像信号处理流程那样清晰的结构,将极大提高代码的清晰性、可读性。

那么必须要考虑的第一个问题是,如何在各个处理步骤间表示数据?这个数据结构能方便的连接各个处理过程,能在它们之间方便的传递,并且有助于每个步骤的处理过程简化,从而逐步接近最终的目标。这个数据结构就是——表!如前面讲的map过程,(map square ls)计算出ls每个元素的平方,然后组成一个表返回。还可以定义filer过程,(filter predicate sequence)将表sequence中满足predicate情形的元素留下,而不满足的过滤掉,最后返回的也是表。还有一个关键的步骤,是枚举过程,即从原始数据结构中以表的形式枚举出所有待处理的元素,然后交由后续过程处理。根据上面所列的步骤,逐步定义enumerate、filter、map和accumulate过程,并且重写sum-odd-squares。
> (define (enumerate-tree tree)    (cond ((null? tree) null)          ((not (pair? tree)) (list tree))          (else (append (enumerate-tree (car tree))                        (enumerate-tree (cdr tree))))))> (enumerate-tree '(a b c ( d (e f))))'(a b c d e f)> (define (filter predicate sequence)    (cond ((null? sequence) null)          ((predicate (car sequence))           (cons (car sequence) (filter predicate (cdr sequence))))          (else (filter predicate (cdr sequence)))))        > (filter odd? '(1 2 3 4))'(1 3)> (filter odd? (enumerate-tree '(1 2 ( 3 4 5) ( 43 5 6(45 (567 897))))))'(1 3 5 43 5 45 567 897)> (define (accumulate op initial sequence)    (if (null? sequence)        initial        (op (car sequence)            (accumulate op initial (cdr sequence)))))> (accumulate + 0 '(1 2 3 4))10> (accumulate * 1 '( 1 2 3 4))24> (define (map proc ls)     (cond ((null? ls) null)              (else (cons (proc (car ls))                              (map proc (cdr ls))))))  > (map sqr '( 1  2 3 5))'(1 4 9 25)
上面定义了几个需要用到的抽象过程,它们可以根据计算目的重新组合,例如sum-odd-squares过程重写后如下:
> (define (newoddsqr sequence)    (accumulate +                0                (map sqr                      (filter odd?                             (enumerate-tree sequence)))))> (newoddsqr '(  2 3 4( 4 ( 5 6 7))))83
这样的高度的模块化设计,使得程序维护变得非常简单。如果要实现求偶数的平方和,那么只要换一个过滤条件就可以了。把odd?改成even?,就可以定义newevensqr过程了。只要改变map的过程参数,就可以对元素进行其他运算,如求立方和等等。






0 0