您的位置:首页 > 其它

SICP第二章学习笔记

2015-04-03 21:39 239 查看
第二章 构造数据抽象(Building Abstractions with Data)

    本章主要讲解数据对象的组合,形成复合数据,以及将复合数据对象的使用与该数据对象怎么由更基本的数据对象构造起来的细节隔离开,也就是数据抽象。使用复合数据可以提升在设计程序中所所谓的概念层次,使数据和过程更接近于自然语言,同时提高程序的模块性。当需要修改数据对象的底层实现时并不需要将整个程序重新修改一遍,只需修改构造程序,其对于使用者来说是隐藏的。

    当利用数据抽象来构建系统时,需要解决复合数据的构造与使用这两方面问题。形成复合数据的关键在于程序设计语言应提供某种“粘合剂”来将数据对象组合起来,而闭包的概念是指用于组合数据对象的粘合剂不但能用于组合基本的数据对象,同样也可以用于复合的数据对象。为了系统能实现通用型操作,则需要抽象屏障技术来处理多种不同表示的数据类型,克服复杂性。

    引入符号数据,实现任意符号作为数据的功能。

    在这一章也表明了过程和数据之间可能没有明显的界限。数据结构也可能是个过程。可以将过程作为对象去操作,数据的过程性表示将在程序设计里扮演一种核心角色。有关的设计风格通常称为消息传递。

    (define (cons x y)

      (define (dispatch m)

        (cond ((= m 0) x)

              ((= m 1) y)

              (else (error "Argument not 0 or 1 -- CONS" m))))

      dispatch)

    (define (car z) (z 0))

    (define (cdr z) (z 1))

    分层设计思想:一个复杂的系统应该通过一系列的层次构造出来。构造各个层次的方法,就是设法组合起作为这一层次中部件的各种基本元素,而这些构造出的部件又可以作为另一个层次里的基本元素。在分层设计中,每个层次上所用的语言都提供了一些基本元素、组合手段,还有对该层次中的适当细节做抽象的手段。

    Scheme涉及语法:cons car cdr序对操作,list构造表结构(nil的值用于表示序对的链的结束),null?操作检查是否为空表,带点尾部记法(最后一个参数前有一个点号,则最后一个形式参数将以所有剩下的实际参数的表为值),map过程,pair?过程检查其参数是否是序对,quote产生符号数据,eq?以两个符号作为参数,检查是否为同样的符号

主要内容:

    数据抽象:将一个过程的的使用方式,与该过程究竟如何通过更基本的过程实现的具体细节相互分离。数据抽象使我们将一个复合对象的使用,与该数据对象怎样由更基本的数据对象构造起来的细节隔离开。程序中使用数据除了完成当前工作必要的的东西外,不对所用数据做任何多余的假设。而一种具体的数据表示的定义,也与程序中使用的数据的方式无关。

    基本思想:为每一类数据对象标识出一组操作,使得对这类数据对象的所有操作都可以基于它们来表述,而且在操作这些数据对象时也只使用它们。

        一种“具体”数据表示的定义与程序中使用数据的方式无关,这两个部分之间的界面将是一组过程,称为选择函数和构造函数。总可以将数据定义为一组适当的选择函数和构造函数,以及为使这些过程成为一套合法表示,它们就必须满足一组特定条件。

        例如有理数,其构造函数:(make-rat <n> <d>)返回一个有理数,其分子是<n>,分母是<d>

            选择函数:(numer <r>)返回有理数<r>的分子,(denom <r>)返回有理数<r>的分母

        构造函数和选择函数在具体表示之上实现抽象的数据。使用数据时可直接操作它们,具体的实现被隐藏了。

    抽象屏障隔离了系统中不同的层次。

        实现方法:在每一层上,这种屏障都把使用数据抽象的程序与实现数据抽象的程序分开来。

层次性数据和闭包性质:

    可以建立元素本身也是序对的序对,这就是表结构得以作为一种表示工具的根本基础。这种能力称为cons的闭包性质。也就是说,它组合起数据对象得到的结果本身还可以通过同样的操作再进行组合。闭包性质使我们能够建立起层次性的结构。

    元素本身也是序列的序列:层次性结构(这种结构由一些部分构成,而其中的各个部分又是由它们的部分构成,并且可以如此继续下去)

    对表的映射:将某种变换应用于一个表的所有元素,得到所有结果构成的表。

    map不仅代表了一种公共模式,而且因为它建立起了一种处理表的高层抽象,即map强调的是从元素表到结果表的一个缩放变换。也就是说,map建立起了一层抽象屏障,将实现表变换的过程的实现,与如何提取表中元素以及组合结果的细节隔离开。

    对树的映射:使用递归的方法遍历一棵树。

    嵌套映射(实现用嵌套循环表述的计算)

与数据结构有关的一种设计原理————使用约定的界面:

    在这里,就是将数据结构有关的操作都表述为序列操作。序列操作形成了一个可以混合和匹配使用的标准的程序元素库。用表实现的序列被作为一种方便的界面,使我们可以利用这种界面去组合起程序元素库中的各种处理模块。模块化结构是控制复杂性的一种威力强大的策略。

    将程序表示为一些针对序列的操作,这样做的价值就在于能帮助我们得到模块化的程序设计,也就是说,得到由一些比较独立的片段的组合构成的设计。

    (这是处理复合数据的一个关键思想:复合数据对象能够成为以混合与匹配的方式组合程序模块的方便界面)

抽象数据的多重表示

    数据抽象使一个程序中的大部分描述能与这一程序所操作的数据对象的具体表示的选择无关。

    多重表示引入:对于一个数据对象也可能存在多种有用的表示方式,而且我们也可能希望所设计的系统能处理多种表示形式。

    实现原理:用抽象屏障去隔离互不相同的设计选择,以便允许不同的设计选择在同一个程序里共存。进一步说,由于大型程序常常是通过组合起一些现存模块构造起来的,而这些模块又是独立设计的,所以需要方法使许多模块结合成一个大型系统而不需要重新设计这些模块。

    实现:构造通用型过程(可以在不止一种数据表示上操作的过程),使数据在一个程序中的不同部分中采用不同的表示方法。构造通用型过程的主要技术是让它们在带有类型标志的数据对象上工作。(此处的通用型过程相当于抽象屏障)

          1、带显示分派(基于类型的分派)的通用型操作:在每个不同的数据结构中包含一个类型标志的部分。因为每个数据对象都以其类型作为标志,选择函数就能够在不同的数据上以一种通用的方式操作。选择函数检查一个数据项的类型,并据此去调用某个适当过程。这种策略在增加新操作或者新类型时都需要改动所有的通用操作。

          2、数据导向的程序设计:在需要处理的是针对不同类型的一集公共通用型操作时,实际上是处理二维表格,其中的一个维上包含着所有的可能操作,另一个维就是所有的可能类型。数据导向的程序设计就是一种使程序能直接利用这种表格进行工作的程序设计技术。其允许孤立地设计每一种数据表示,而后用添加的方式将它们组合进去,不需要任何修改。这种策略很方便得通过包机制增加新类型和新的通用操作。

          3、消息传递:将数据导向中的表格按列分解,将每一个数据对象表示为一个过程,它以操作的名字作为输入,能够去执行指定的操作。这种策略可以很方便得增加新类型,但如果增加新操作,需要把已有的实例全部重新实例化一遍才能使用新操作。

    带有通用型操作的系统的构造方法:通过通用型界面过程,将描述数据操作的代码连接到几种不同表示上。

    要使跨过类型界限的操作也能有意思,则需要组合不同类型数据:1、为每一种类型组合的合法运算设计一个特定的过程;2、把一种类型的对象看作另一种类型的对象,这种方法叫做强制。(可以根据类型的层次结构进行转换)

区间算术:

    作为可以用于组合“区间”(表示某种不准确量的可能值的对象)的一组算术运算。两个区间的加减乘除的结果仍是一个区间,表示计算结果的范围。

八皇后问题

    假定我们已经生成了在棋盘的前k-1列中放置k-1个皇后的所有可能方式,现在需要的就是对于其中的每种方法,生成出将下一个皇后放置在第k列中每一行的扩充集合。而后过滤它们,只留下能使位于第k列的皇后与其他皇后相安无事的那些扩充。这样就能产生出将k个皇后放置在前k列的所有格局的序列。

集合的表示

    集合作为未排序的表

    集合作为排序的表

    集合作为二叉树(二叉排序树)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: