2010-12-112010-12-11第二章没有做完,未来会更新。
总结1:
The general techinque 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. We will see how data abstraction makes programs much
easier to design, maintain, and modify.
Abstraction:
We noted that a procedure used as an element in creating a more complex procedure could be regarded
not only as a collection of particular operations but also as a procedural abstraction. That is, the
details of how the procedure was implenmented could be suppressed, and the particular procedure
itself could be replaced by any other procedure with the same overall procedure.
The analogous notion for compound data is called data abstraction. Data abstraction is a methodology
that enable us to isolate how a compound data oject is used from the details of how it is
constructed from more primitive data objects.
Exercise 2.1
背景知识:在习题之前的内容书中介绍了数据结构的两个方面,数据组成representation+接口(selectors and
constructors),最开始的例子make-rat由(cons n d)生成的pair,它的seletor and constructors包括make-rat,
numer, denom三个函数。其他所有操作函数都要通过selectors and constructors来操作和访问这个pair数据。
问题描述:
代码位置:ration_data.scm
Exercise 2.2
背景知识:在习题之前的内容书中介绍了数据结构的两个方面,数据组成representation+接口(selectors and
constructors),最开始的例子make-rat由(cons n d)生成的pair,它的seletor and constructors包括make-rat,
numer, denom三个函数。其他所有操作函数都要通过selectors and constructors来操作和访问这个pair数据。
问题描述:
代码位置:segment.scm
Exercise 2.3
背景知识: Data-abstraction Barriers。一般来说,数据结构设计都有分层的概念。分层的作用在2.1.2节有具体
介绍。分层的一般方法是:
1 应用逻辑
2 constructors and selectors
3 基础数据结构接口
2、3表示representation, representation = specified conditions + constructors and selectors
分层的基本原则是将变化单独封装,单层的改变不影响其他代码的运行,高层代码调用下层的接口,下层接口不影
响上层逻辑的运行。
Exercise 2.4
背景知识:pair的proceduarl representation。不用数据结构而只用函数来表示pair的基本功能(car (cons x
y))= x 和(cdr (cons x y)) = y,以此来显示scheme的强大的procedure representation功能。SICP 2.1.3的一
段话表示:procedural representations of data will play a central role in our programming
repertorire, this style of programming is often called messaging passing, and we will be using it as
a basic tool in chapter 3 when we address the issues of modeling and simulation.
Exercise 2.5
问题描述:没有看懂题目。
Exercise 2.6 [未完成unfinished]
背景知识:church numeray显示scheme强大的函数功能,即使不直接用数字,用函数也能表示数字功能
问题描述:好像有点意思,是为了体现设计哲学,还是纯的智力练习捏?
问题求解:从substitution model evaluate中寻找模式。最后居然被我找到了!
(define zero (lambda (f) (lambda (x) x)))
因为(add-1 zero) = (lambda(f) (lambda(x) (f x)))
所以(define one (lambda(f) (lambda(x) (f x))))
(add-1 one) =(lambda(f) (lambda(x) (f (f x))))
所以(define two (lambda(f) (lambda(x) (f (f x)))))
大致意思我知道了。但是如何定义add函数捏,不从add-1的repeat的角度来设计。这个我还不会!
代码位置:datstruct.scm
Exercise 2.7
问题描述:只是实现selectors而已,lower-bound=car upper-bound=cdr
代码位置:interval-arithmetic.scm
Exercise 2.8
问题描述:很简单.
代码位置:interval-arithmetic.scm
总结2:
closure, closure is the key to power in any means of combination because it permts us to create
hierarchical structures----structures made up of parts, which themselves are made up of parts, and
so on。
Map is an important construct, not only because it captures a common pattern, but also it establishs
an higher level of abstraction in dealing with list. In effect, map helps estabish an abstract
barrier that isolate the implemention of procdures that transfrom lists from the details of how the
elements of list are extracted and combined.
Exercise 2.17
问题描述:很简单.
代码位置:list.scm
Exercise 2.18
问题描述:比较简单.关键在于实现的好坏!这里有一个O(n^2)的简单实现
(define (reverse items)
(define (rev-rec items count)
(if (= count 0)
nil
(cons (list-ref items count) (rev-rec items (- count 1)) )))
(rev-rec items (length items)))
如果是C语言,我觉得可以很好的使用O(n)的算法来实现。只要通过[n/2]次交换就可以完成任务了。但是在
scheme语言中不太好做到这一点。
Exercise 2.19
问题描述:很简单.
代码位置:list.scm
Exercise 2.20
问题描述:用scheme的变参数函数来编码
代码位置:list.scm
Exercise 2.21
问题描述:很简单.就是使用lambda(x) (* x x)作为map函数的参数。
代码位置:list.scm
Exercise 2.22
问题描述:这里有一个recursive process和itersive process处理list的区别,也是recursive process处理list
一个优势,那就是,使用itersive process的方式来编写map函数,得到的结果list与目标list的顺序是反序的。
习题表达的就是这个意思!
Exercise 2.23
问题描述:很简单.跟map的实现类似,就是不用把结果保存在串中。
Exercise 2.25
问题描述:就是查找tree的为7的节点
问题解决:如果可以使用pair?函数的话,似乎很简单,但是题目中似乎只说用car cdr的组合。
Exercise 2.26 [有疑惑]
问题描述:比较简单,但是澄清了我的疑惑,cons list append的区别是什么。
(define x (list 1 2 3))
(define y (list 4 5 6))
(cons x y) = ((1 2 3) 4 5 6)
(list x y) = ((1 2 3) (4 5 6))
(append x y) = (1 2 3 4 5 6)
在解释器中尝试下列基础运算:
(cons 1 (cons 2 (cons 3 4))) = (1 2 3 . 4) 还有一个疑惑,这个点是什么意思捏?
(cons () (cons 2 (cons 3 ()))) = (() 2 3)
(cons 1 (cons 2 (cons 3 (cons4 ())))) = (1 2 3 4) = (list 1 2 3 4)
关于这个疑惑,我作如下解答:
(cons 1 2) = (1 . 2)
(cons 1 (cons 2 ())) = (1 2)
(1 . 2) 与 (1 2)在文法上是有区别的,在Box-and-pointer图中表示也是有区别,但是对于使用来说,区别不是很
大。留待观察。
Exercise 2.27 [有待改进]
问题描述:在2.18的结果上作改进,使其对tree使用,能够对每个节点的树叶都进行交换。
在写复杂的过程的时候,我总是想着用 if else if结构,在scheme中,这是ill form的写法。
可以用cond 语句来替换。
写复杂的过程时候才真正理解递归比c形式的for循环的优势,一切问题都可以由递归来作求解,虽说循环/迭代的效率高,但是在实现和证明程序的正确性甚至算法分析方面,递归的优势不是一点点。现在知道为什么说计算机的基础是递归+指针了。
代码位置:list.scm reverse的实现效率很低,deep-reverse效率更低,好难看的一陀代码!
Exercise 2.28
问题描述:比Excercise2.27要简单很多,实现也容易
代码位置:list_map.scm
Exercise 2.29 [未完成]
问题描述:应该很有意义的,我还没有作。
Excercise 2.30 [重要]
问题描述:很有意义显示了高阶函数的作用了,如果要把map的功能和lambda函数的功能放在一个函数中,那这个
函数就没有办法读了
Excercise 2.31
问题描述:只是定义一个tree-map函数,意义不是很重大。
代码位置:list_map.scm
Excercise 2.32 [未完成]
问题描述:看来又是一个智力题目,将集合所有的子集建立一个集合。
Exercise 2.33 [未完成]
问题背景:convention interface
fliter函数输入为sequence和predicate,输出为sequence满足predicate条件的子sequence
map 函数输入为sequence和proc,输出为分别对sequence每一项进行proc过程调用的结果组成的串。
accumulate函数是可以为list/tree的数据结构定制操作的接口,accumulate的参数是op init和
sequence, sequence 可以是map和filter函数的结果。是参与accumulate过程的sequence。op是对sequnce进行累
积操作的形式。一般来说,这个操作形式不应该太复杂。Exercise 2.22和Exercise 2.23两个题目是使用
accumulate来重写map length 和append函数,可以知道的是,在accumulate的内部op是有两个参数的过程。第一
个参数是(car sequence) 第二个参数是(cdr sequece)为参数的结果。所以在编写op的形式的时候要注意op为
(lambda (x y) <>) 中的x为输入的(car sequence),y是以(cdr sequence)作为参数的结果。知道这一点,op的编
写就十分简单了。
enumerate函数输入为tree/sequnce,将输入扩展为List。
问题描述:使用list的conventional interface来实现常用的list 操作接口。
代码位置:convention_inf.scm
Exercise 2.34
Exercise 2.35
问题描述:和2.33一样,虽然我可以实现正确的功能,但是我总觉得现在的做法不是最好的!
代码位置:convention_inf.scm
Exercise 2.36
问题描述:accumulate函数的扩展。
代码位置:convention_inf.scm
Exercise 2.37 [未完成]
问题描述:矩阵或者向量的基本运算。
代码位置:
Excercise 2.38
问题描述:提出了sequence循环操作的方向的概念,Left-fold VS right-fold,对于一些运算来说,比如car list,
left-fold和right-fold结果是不一样的。
fold-left 的状态迁移: result <- (op result (car rest))
rest <- (cdr rest)
扩展为 result <- ((op (op init s1) s2) ...),sequence的元素从左到右参与计算
fold-right的最终扩展为 result <- ((op (op init sn) s(n-1)) ...)
其实就是fold-left使用递归的方式来遍历操作list,fold-right是使用迭代的方式来遍历操作list.
Excercise 2.39
问题描述: 用fold-right和fold-left来实现reverse. 以(list 1 2 3 4)为例子说明问题。
fold-left的使用fold-left的方式超级简单,因为它本身就是从4-3-2-1的方式结合实现reverse的,按照(cons 4 (cons 3 (cons 2 (cons 1 nil))))
的方式就可以得到一个list,所以,只要使用cons作为op来进行fold-left就可以了。
fold-right从1-2-3-4的方式进行结合,必须使用(append (append (append (append nil (list 4)) (list 3)) (list 2)) (list 1))的方式
才能得到(4 3 2 1)的list,所以要使用append。
本题再一次显示了append和cons的差别。习题2.26也显示了这一点。
代码: