0%

读SICP心得

《计算机程序的构造与解释》是MIT的计算机入门书。

​ SICP这本书采用的是scheme(lisp语言家族)语言教学。很多人入门计算机的时候纠结语言的语法,这无疑是舍本逐末。而lisp这个短小精悍的语言向我们展示了完成程序设计根本不需要多少花里胡哨的语法。控制表达式仅需要cond或if(cond 和 if可以相互转化),过程定义需要lambda,外加一些简简单单的基本过程。

​ 粘合数据的方法也仅靠基本过程cons、car、cdr,没有其他语言的struct、class,就是简简单单的序对就可以组合成任意一种抽象数据。有种道生一,一生二,二生三,三生万物的玄妙感。

​ 这本书开门见山地告诉我们,对于一个程序设计语言,先掌握构成她的基本元素、组合方式、抽象手段。

​ 在介绍lisp语法之外,告知我们递归计算过程和迭代计算过程的区别,这一区别不在语法意义上,而是在计算意义上的。迭代计算过程中,函数的参数就已经保存了所有需要的状态,而递归计算过程,需要解释器或其他方法来隐式地维护中间的状态。

​ 还简明扼要的介绍了函数式编程,其实就是本书的第一个计算模型——代换模型。在这一模型中, 过程对于相同的参数输入,总是有相同的值,这使得我们可以一步步分解复杂的过程为基本过程的组合,介绍了map,fold,filter等函数式编程中的重要过程。

​ 到了第二章,介绍了一个重要的概念闭包。这个闭包与抽象代数中,运算符的封闭性类似。一个类型的数据经过某种操作,组合还是这一类型,然后利用这一特性,不断构建更复杂的东西。

​ 然后介绍了抽象数据的分层,这是掌控程序设计复杂性的重要方法。随后,向我们揭示了,在函数式程序设计中,表示一个数据仅需要她如何构造,如何获得她的组成部分即可。

​ 随后进一步了解类型系统的设计,介绍了一种重要的方法——消息传递。这一设计方法,像面向对象语言中提供的object.act()语法。同时有介绍了塔型的类型层次结构,跟面向对象概念中的基类,派生类类似。可见程序设计的思想跟用什么语言是无关的。

​ 代换模型虽然简单,而且似乎也够用了。但是当我们把程序联系到现实生活,就会发现编程的困难。因为人类把世界看成一个一个的对象,他是有状态的。因此我们引入了一个新的操作——赋值操作,通过这个操作来改变所要联系的实体的状态。同时代换模型已经解释不了赋值操作对程序设计带来的改变了,于是介绍了另一种计算模型——环境模型。赋值操作给模拟现实世界带来了方便,但也带来大量的问题,最重要的就是时间问题。用赋值操作,就不得不考虑时间发生的先后,而函数式编程显然没有这个问题。于是本书给我们初步介绍了并发问题,提出了用互斥量解决其中的一部分。

​ 随后,又像我们介绍了流,流可以看做延时求值的表。只有用到cdr的时候,cdr部分才求值。然后定义流的map,fold,filter,这样就能营造一种无穷表的假象,按需计算。同时强迫求值时,还将结果缓存,避免多次求值。

​ 到了本书最震撼的部分了。本书指导我们用lisp写一个lisp求值器,实现自举。这一部分能让你领悟了lisp采用s表达式的形式的妙处,实际上输入一个s表达式,lisp解释器会把他解析成序列的结构,不需要我们再做词法分析,同时由于lisp表达式是如此简单,我们仅需一通car,cdr就能提取嵌套的表达式,很方便就能设计解释器。本书带我们实现了一个迷你的解释器,解释了本书的封面,eval和apply的循环,eval 向 apply 传递过程和参数,apply向eval传递环境和表达式,他们互相调用,又自己调用自己。让人情不自禁的想到太极图,衔尾蛇,艾舍尔的《画手》,这真的是最让人感到震撼的地方,一个语言竟然自己实现了他自己。

​ 随后,本书指导我们为我们实现的迷你lisp添加特性,如动态作用域,惰性求值。告诉我们一个语言的特性并不是越多越好,每个特性之间会相互影响,这些特性的组合可能对程序设计来说是一场灾难(说的就是你C++)。

​ 之后带我们实现了一个逻辑程序设计语言,主要利用模式匹配方法。用and、or、not组合,并且可以为组合命名。并且提出了封闭世界假说“凡是认知外的都不是真的”。

​ 最后一章揭开了lisp语言的神秘面纱,用寄存器机器来执行过程。首先是用寄存器机器来执行gcd算法

1
2
3
4
(define (gcd a b)
(if (= b 0)
a
(gcd b (remainder a b))))

​ 我们可以看到他的返回值的形状与该函数一样,那我们只需简单的把数据在寄存器上移动就可以执行这一过程,这就是迭代计算过程。

随后演示了阶乘的递归计算

1
2
3
4
(define (factorial n)
(if (= n 1)
n
(* n (factorial (- n 1))))

​ 我们注意,这不能通过数据在寄存器上的移动来完成计算,因为寄存器始终是有限的,而我们factorial机器里面嵌套着factorial机器,因此我们需要一种额外的数据结构——栈来保存当前机器的状态,然后执行下一个机器。用有限的东西来营造了无穷的假象。

之后又带我们了解了垃圾回收的两个简单算法。

这本书的第四章和第五章很难,习题就有用lisp描述一个机器语言,然后把lisp编译成这个机器语言,由于自身的能力原因,我还不能完成这项困难的任务。

​ 纵观全书,环环相扣,引人入胜。恰到好处地介绍了,程序设计中常遇的问题,又给更深层次的研究(垃圾回收,并发编程,面向对象,软件工程,编译原理,函数式编程,编程语言设计)埋下了伏笔,不愧是优秀地计算机入门书。