SICP学习小结

我一开始在看CSAPP,刚看完第一章,然后看见知乎上萧井陌发表的关于编程入门的文章。他的推荐是先掌握一门语言比如Python然后看SICP,接着才是CSAPP。我查了一下发现只有400多页,就打算20天看完这本书,结果一不小心用了40天。虽然是一门入门书,但发现这本书讲的内容真不少。

我读第一遍的时候还是想着赶进度,看完一章后对照着SICP解题集做一遍习题。到了第四章后就是单纯得看书了。发现到后面越来越吃力,虽然最后勉强读完了,但感觉没有什么收获。于是开始了第二遍的阅读,同时在总结并记笔记,这也是这几篇博文的由来。

萧大对这本书的介绍:

在阅读SICP之前,你也许能通过调用几个函数解决一个简单问题。但阅读完SICP之后,你会学会如何将问题抽象并且分解,从而处理更复杂更庞大的问题,这是编程能力巨大的飞跃,这会在本质上改变你思考问题以及用代码解决问题的方式。此外,SICP的教学语言为 Scheme,可以让你初步了解函数式编程。更重要的是,他的语法十分简单,你可以很快学会它,从而把更多的时间用于学习书中的编程思想以及复杂问题的解决之道上。

彼得·诺维格的书评(渣翻译,请见谅)

To use an analogy, if SICP were about automobiles, it would be for the person who wants to know how cars work, how they are built, and how one might design fuel-efficient, safe, reliable vehicles for the 21st century. The people who hate SICP are the ones who just want to know how to drive their car on the highway, just like everyone else.

如果说SICP相当于汽车,那么,想知道汽车是如何运作的,制造过程是怎么样的,当下怎么设计才能让它省油、安全、可靠的人将在书中得到答案。如果你只想知道如何像其他人一样在路上开车,那么这本书对于你来说毫无用处。

本书涉及的关于Scheme的语法比较少,刚好满足作者讲明白他想要表达的思想。所以如何想学习Scheme这一语言的话不应该看这本书。前三章一直围绕着如何设计计算机程序,层层深入又相互联系。
第一章侧重于描述过程。过程的组合可以产生复合的过程。过程的抽象使得我们不用对一些过程的细节重复实现而可以直接调用过程完成我们的工作。一些过程可能共享着一套公共模式,将这种模式抽象出来,实现为高阶函数可以来表述计算的一般性过程。

   例如,当我们需要在一个程序的许多地方将一个序列中的所有数字进行求和,我们可以实现一个过程,向这个过程传入一个序列,遍历序列中的每个数字,进行求和。那么在需要的地方我们调用这个过程,而不用重复去实现。假设还有一个程序,它将一个序列中的所有素数打印出来。这两个程序都是遍历一些数据,然后对每个数进行一种操作。我们可以将这个共同模式提取出来实现为一个过程。这个过程的参数是一个序列和对每个元素执行的那个操作。我们在实现求和过程后降低了程序的复杂度,在实现高阶过程后又降低了求和过程和查找素数过程的复杂度。

第二章主要讲解数据对象的组合,形成复合数据,以及将复合数据对象的使用与该数据对象怎么由更基本的数据对象构造起来的细节隔离开,也就是数据抽象。

    在我们实现复数的加法运算过程时,如a+ib和c+id,参数是a、b、c、d,返回的结果是a+c,b+d。的确实现了目标,但很难将过程和其用途联系起来。我们将a和c组合一起,定义为一个复数x,(car x)是实数a,(cdr x)是虚数b,这样我们传入了两个复数x、y,返回的结果是(car x)+(car y),(cdr x)+(cdr y)。这就是数据的组合。将(car x)定义为numer,将(cdr x)定义为denom。这时,我们相当于在复数的层次上进行各种操作,复数的实现被隐藏起来了,这就是数据的抽象。数据的组合和抽象使程序的可读性提高,同时降低了我们在更高的层次上构造程序的复杂度。

过程与数据之间也不是绝对对立的,数据也可以用过程实现。
拥有了过程与数据的抽象和组合技术后,我们可以较为轻松地构造更加复杂的程序。但这还不够,还需要一些组织原则,需要能够帮助我们构造起模块化的大型系统的策略。
第三章介绍了一种设计策略,就是基于被模拟系统的结构去设计程序的结构。它的优势是,对任何一个模块的修改都是局部的,不需要全局代码的修改。
其中一种方法是将一个大型系统看成一批对象,它们的行为可能随时间的进展而变化。每一个计算对象必须有自己的一些局部状态变量。也就是将这一世界模拟成一集相互分离的、受时间约束的、具有状态的相互交流的对象。

   看一二章时感觉编码风格就像是面向过程编码。一个系统就是由一个个抽象过程组成,而一些过程的参数可以是一个个抽象数据,然后执行一些操作,无法很好地体现数据和过程之间的关系。而看第三章的开头时,突然发现风格转变为了面对对象编程。类似C++中的类,局部状态变量就相当于类的数据成员,内部过程就相当于成员函数,同时有新增了赋值操作使数据不再是一成不变的。这种设计方法是程序更加结构化,也更加安全,更为接近真实的物理系统。而过程和数据抽象在这一个策略下仍然适用。

另一种方法是建立流模型,可以模拟一些包含状态的系统却不需要利用赋值或变动数据。引进了一种延时求值的技术,可以看作是一种“需要驱动”的程序设计,松开了被模拟的世界中的时间与求值中事情发生的顺序之间的紧密联系。也就是将这一世界模拟为单一的、无时间也无状态的统一体。

   两种方法有各自的优势,也有各自的短板,但作为设计策略,它们都能使我们更加接近真实的模拟系统,构造大型程序。例如,我们可以输入数据到计算器中,选择需要的操作的按钮来执行操作。那么,当只有过程与数据抽象技术:分别定义一些过程,传入数据,返回结果;对象模型:将这些过程组织成一个子系统,数据可以作为内部状态变量,计算过程将改变它们的值;流模型:将初始值和一系列需要执行的过程作为一个流,生成计算结果流。

这本书就这样一步一步得教你如何降低构造大型程序的复杂度。但这不仅仅是介绍软件工程的书。后面两章分别从两个角度来介绍语言的实现。

    第四章主要是实现了一个元循环求值器。用Scheme来对Scheme程序进行解释。基本循环涉及语法分析,关于内部定义的部分提及扫描程序。通过维护一个环境表来实现环境模型,用来对表达式进行求值。lisp的代码即数据的思想。语法分析与执行分离优化程序执行效率。
    第五章则从底层的角度,介绍了Scheme的各种语法过程是如何在寄存器层次上实现的,并将Scheme翻译为一种更接近计算机硬件的语言。将存储看作变量,使我们实现了序对。废料收集是存储管理的一部分。

四五章讲述了简单的编译原理方面的内容。由于我们是以Lisp作为教学语言进行的讲解,这种局限性也导致了涉及编译原理方面的知识较为有限。

我们可以从软件工程和编译原理的角度看这本书。而由于作者由浅入深的讲解顺序,我们可以从第三个角度再了解一遍全书。
这本书仿佛在设计一个语言。一开始我们的语言只是过程,主要用来解决一些数学问题。当数据太多,我们想要格式化得表述一些数据,体现单一数据之间的联系,然后我们便引进了序对。现实中的数据不仅仅是数字,还有字符数据,于是引入了引号表达式。再后来的内部状态变量,流等等。第四章二三节教我们如何修改或新增基础语言语言。我们设计了越来越多的语法,为了更好得解决我们可能会遇到的问题。为了实现它们,我们使用了正则序和应用序,后来用了环境模型。而四五章就是讲解我们设计的语言如何在计算机上成为可能。

用计算机解决问题:设计语言构造更加模块化的系统,编译原理完成语言的实现。这也就是这本书的书名,计算机程序的构造和解释。

在扯回构造程序部分,作者笔下的很多实用的设计思想值得我们学习和掌握。分层设计思想将一个复杂系统通过一系列的层次构造出来,消息传递使我们将过程像类一样操作,而语法糖衣又令其表现为单纯的过程等等。这些方法都能很好得降低复杂度。
此外,本书涉及的方面也比较多,很多数学方面的知识比如牛顿法,寻找不动点方法,保证概率上正确的算法,伪随机数;逻辑程序设计与数据库的实现有一定的关系;哈夫曼编码等等。我留了三个坑,打算读完CSAPP后来实现。也就是3.3节的数字电路模拟器和约束的传播。4.1节元循环求值器的完善。

我的看法:这本书可以给你展现一个世界,通过总结和分析可以使你对这一方面的东西有一个清晰的认识。如果读完你只是觉得是用Scheme实现一个个程序,那你不会有大的收获。4.1节我也认为是最精彩的一部分。有人说第五章没有看的必要,应该是仁者见仁智者见智吧。我第一遍的时候第五节后面部分读得蒙蒙懂,第二遍又是草草得读。不过我以后还要学习编译原理,可以到时候在仔细看下。但作为一本奇书,它的深度比较有限,我觉得优点过于吹捧,不过还是那句话,每个人的看法不同。而且受我自己对计算机直到的了解的限制(学生,好多只是了解,更多的东西根本不懂),可能这本书还有一些部分我没体会到。

你可能感兴趣的:(sicp)