关于《计算机程序的构造和解释》
關(guān)于《計(jì)算機(jī)程序的構(gòu)造和解釋》
來源 http://www.nowamagic.net/librarys/veda/detail/1905
?
先談?wù)勱P(guān)于《計(jì)算機(jī)程序的構(gòu)造和解釋》(后面簡(jiǎn)稱為SICP)的幾個(gè)八卦。
- 本書曾經(jīng)是MIT本科第一門課的教材。前兩年被Python取代,在geek中引發(fā)了軒然大波。有興趣可以Google一下[sicp mit python]。
- 本書在Amazon上的評(píng)分嚴(yán)重兩極分化,五星(>90)和一星(>50)為主,徹底反正態(tài)分布。
- 本書在Amazon上排名最高的書評(píng)出自Peter Norvig,當(dāng)然是強(qiáng)烈推薦,順便狠狠地鄙視了給一顆星的同學(xué)們;第二篇出自Paul Graham,還是強(qiáng)烈推薦。
- 本書別名紫書(The Purple Book),巫師書(The Wizard Book),或者干脆The Book。
這是一本什么樣的書?
前言說,這是一本給MIT學(xué)生的入門級(jí)(entry-level)計(jì)算機(jī)科學(xué)教材。作者的出發(fā)點(diǎn)有兩條:?
- 語言首先是寫給人看的,只是恰巧(incidentally)能夠運(yùn)行。這當(dāng)然是個(gè)修辭,格外強(qiáng)調(diào)代碼之可讀。
- 語言的語法,漂亮的算法,數(shù)學(xué)的分析,這些統(tǒng)統(tǒng)都不重要。最打緊的是如何控制復(fù)雜度(The techniques used to control the intellectual complexity of large software systems)。
在本書成書的年代(1984),以上言論即使不算正邪不兩立,也夠的上離經(jīng)叛道了。
通俗的說,這本書教你如何用最基本的構(gòu)造和原則,解決復(fù)雜和多樣的問題。用攝影打比方,這本書不比較尼康和佳能,不介紹繁雜的機(jī)型和參數(shù),不介紹后期處理的技巧。這本書只討論光線、色彩和構(gòu)圖,以及如何在不同場(chǎng)景拿捏這些基本原則組合出美妙的照片。
?
《計(jì)算機(jī)程序的構(gòu)造和解釋》(更多推薦……)
這本書適合初學(xué)者嗎?
不好說。Amazon上的一顆星評(píng)價(jià)大多鄙視本書已經(jīng)過時(shí)或者太過高深。我個(gè)人看法,它很適合一部分初學(xué)者,但是需要滿足幾個(gè)條件:?
- 熱愛計(jì)算機(jī)科學(xué)?
- 有時(shí)間和耐心?
- 受過(高中水平)數(shù)學(xué)和抽象思維的訓(xùn)練?
所以,如果只是想領(lǐng)一份程序員的薪水,這本書完全可以略過。并不是說這本書有多么不實(shí)用,只是計(jì)算機(jī)科學(xué)與寫代碼并不是一碼事。
這本書廣而不深,討論到了非常多重要的思想,有些甚至冷不丁出現(xiàn)在注釋里(比如Y算子)。內(nèi)容安排很照顧初學(xué)者,循循善誘;語言直白簡(jiǎn)單;代碼大多簡(jiǎn)明自然。
至于習(xí)題,個(gè)人認(rèn)為只要認(rèn)真思考,大部分都不是很難,需要耐心多于智力。有時(shí)間不妨多做幾道。
這本書適合有經(jīng)驗(yàn)的程序員嗎?
還行。如此庖丁解牛般的講解,其他書中不多見;內(nèi)容簡(jiǎn)單,思想?yún)s不過時(shí)。另外,國(guó)內(nèi)絕大部分程序員都從命令式語言入門,不妨接觸一些函數(shù)式思想,開開眼界。如果時(shí)間不多,至少看看前兩章,學(xué)習(xí)一些解決復(fù)雜問題和編寫優(yōu)雅代碼的技巧。
為什么我們要學(xué)習(xí)這本書?因?yàn)檫@本書告訴我們?nèi)绾纬橄蟆槭裁次覀円獙W(xué)習(xí)如何抽象?因?yàn)槌橄笫俏覀兛刂栖浖?fù)雜性的重要手段。軟件是人類有史以來最復(fù)雜的系統(tǒng)。其一、軟件系統(tǒng)本身規(guī)模龐大,參與人手眾多,難以管理;其二、環(huán)境和需求不斷變化,且錯(cuò)誤難以避免。
人類無法駕馭過于復(fù)雜的事物,于是只能尋找方法簡(jiǎn)化軟件系統(tǒng):把系統(tǒng)分為許多子部分,人們開發(fā)一個(gè)部分的時(shí)候,系統(tǒng)其他部分都是一種抽象,無需了解其細(xì)節(jié)。
本書討論的就是系統(tǒng)的組織和設(shè)計(jì),有哪些方法可以幫助我們控制軟件的復(fù)雜度。
Scheme好學(xué)嗎?
其實(shí)Scheme是一門異常簡(jiǎn)單的語言。直來直去,除了括號(hào)多,基本沒有旁門左道(比如指針)和撕心裂肺的語言構(gòu)造(比如模版、多重繼承、Monad)。再者,這本書的內(nèi)容和具體的語言基本沒有關(guān)系,思想才是重頭戲。如果實(shí)在有顧慮,推薦先讀兩本薄薄的小冊(cè)子:The Little Schemer和The Seasoned Schemer。
這本書到底講什么?
本書按照內(nèi)容可以分為三個(gè)部分:過程抽象(第一章);數(shù)據(jù)抽象(第二、三章)和語言抽象(第四、五章)。
過程抽象部分比較簡(jiǎn)單,先介紹了Scheme的基本語法,讓讀者初步領(lǐng)略函數(shù)式編程的風(fēng)采。對(duì)于有一定編程基礎(chǔ)(相信國(guó)內(nèi)極少有人入門就讀這個(gè))的讀者來說,會(huì)有耳目一新的感覺,原來遞歸和迭代可以有另一種表現(xiàn)形式,但并不難理解。習(xí)題也比較簡(jiǎn)單,不會(huì)用掉太多的時(shí)間。過程抽象的概念也很簡(jiǎn)單,就是編程語言中的函數(shù),目的是封裝計(jì)算過程的細(xì)節(jié)。關(guān)于何時(shí)應(yīng)該用過程抽象的原則是:一切可以定義為過程的計(jì)算片段都應(yīng)該定義為過程。
數(shù)據(jù)抽象是我認(rèn)為的本書的核心,也是最值得我們仔細(xì)研讀的部分。關(guān)于數(shù)據(jù)抽象最直接的理解就是面向?qū)ο缶幊?#xff0c;如C++,而Java和C#則是更徹底的數(shù)據(jù)抽象。把一組過程抽象(類的方法)集中考慮,并加入內(nèi)部狀態(tài)(類的變量),就是一個(gè)數(shù)據(jù)抽象。每個(gè)數(shù)據(jù)抽象都應(yīng)該把自己的內(nèi)部對(duì)象狀態(tài)和對(duì)象的實(shí)現(xiàn)隱藏起來,對(duì)外通過一組接口進(jìn)行消息傳遞。這樣聽起來好像本書與一般的面向?qū)ο髸鴽]有區(qū)別,但實(shí)際上,這些都是我自己的總結(jié),書里面不會(huì)把這些概念直接羅列出來,而是通過一個(gè)個(gè)巧妙的例子,讓讀者一步步深入,感嘆原來A還可以這樣抽象,原來B還可以這樣封裝。個(gè)人認(rèn)為如果時(shí)間有限,讀完前三章已經(jīng)可以領(lǐng)會(huì)本書大部分思想了,后兩章可以不讀。
語言抽象是指自己發(fā)明一門語言,以解決某一特定應(yīng)用領(lǐng)域的問題。在這一領(lǐng)域中,自己發(fā)明的語言會(huì)比其他通用語言更方便。定義了新語言的語法后,就要自己去實(shí)現(xiàn)該語言的編譯器或解釋器,可以通過現(xiàn)有的語言去構(gòu)造。這一部分包含了許多編譯方面的知識(shí),但又與編譯原理中的構(gòu)造方法有不少區(qū)別,自己看書很容易看得云里霧里,聽老師講課才好一些。大部分習(xí)題很難做,一部分習(xí)題非常難。
第一章討論程序設(shè)計(jì)的最基本原則:原語(primitive expressions)、組合(means of composition)和抽象(means of abstraction),以及如何利用這些基本原則化解復(fù)雜度。重點(diǎn)是過程抽象和高階過程(high-order procedures)。本章的例題十分精彩,抽象和組合的過程十分清晰。有關(guān)遞歸和迭代的討論也非常耐讀。
第二章討論數(shù)據(jù)抽象,即利用基本數(shù)據(jù)構(gòu)造復(fù)雜結(jié)構(gòu)。Scheme里的基本構(gòu)造能力只有cons,但由此可以組合出所有實(shí)用的結(jié)構(gòu)。圖像語言、符號(hào)運(yùn)算、集合表示、哈夫曼編碼和復(fù)數(shù)系統(tǒng)都是經(jīng)典實(shí)用的例子。順帶還介紹了data-directed方法,與面向?qū)ο笾械姆庋b有異曲同工之妙。
即使沒有太多時(shí)間,我覺得前兩章也值得值得細(xì)讀。尤其是例子。
第三章主要討論了狀態(tài)(local state)和環(huán)境(environment model),可變數(shù)據(jù)結(jié)構(gòu)(mutable data),以及狀態(tài)和時(shí)間的交互(concurrency和laziness)。前兩章用到語言是Scheme的一個(gè)沒有副作用的子集,從這一章開始涉及解釋器的核心機(jī)制,尤其是狀態(tài)的管理,及其優(yōu)缺點(diǎn)。
第四章用Scheme實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Scheme解釋器。重點(diǎn)是討論語言的解釋過程,以及如何針對(duì)問題(領(lǐng)域)創(chuàng)造和修改語言,從中可見DSL(Domain Specific Language)的思想。后三節(jié)各自討論一個(gè)工程中不常見但高效解決特定問題的語言變種及其實(shí)現(xiàn)。
第五章介紹將Scheme編譯為現(xiàn)實(shí)中的寄存器機(jī)器模型(register machine)。重點(diǎn)不是編譯技巧(Scheme壓根不需要文法分析),而是基本構(gòu)造(條件、過程,等等)對(duì)應(yīng)于寄存器模型的實(shí)現(xiàn)。略帶討論了最簡(jiǎn)單的垃圾回收。
后三章較深,最好略有一點(diǎn)語言、編譯和體系結(jié)構(gòu)的基礎(chǔ),或者多些耐心。
語言會(huì)影響思維
如果要問現(xiàn)代數(shù)學(xué)最重要的概念是什么,那毫無疑問就是函數(shù)了,或者更確切地說,是映射。泛函這個(gè)詞,或許對(duì)非數(shù)學(xué)系的同學(xué)來說有些陌生,但如果寫成英語 functional, 看起來就眼熟多了。狹隘一點(diǎn)地說,泛函就是以函數(shù)為參數(shù),返回值是數(shù)值的一類函數(shù)。看到這里相信不少同學(xué)都發(fā)現(xiàn)了,這就是在很多語言中被稱為高階函數(shù)(high-order function)的那個(gè)東西。泛函在數(shù)學(xué)中是如此普遍的概念,現(xiàn)代數(shù)學(xué)幾乎無處不會(huì)用到。數(shù)學(xué)家們很自然地在集合上添加運(yùn)算,構(gòu)造空間;從一個(gè)空間映射到另一個(gè)空間,創(chuàng)造泛函。對(duì)泛函做變換,構(gòu)造泛函的泛函等等。
為什么我要在這里提到數(shù)學(xué)和泛函?因?yàn)樵谖铱磥?#xff0c; lisp 是一門以表達(dá)數(shù)學(xué)為己任的語言。正如?SICP?中希望表達(dá)的一種觀點(diǎn):語言會(huì)影響思維。如果數(shù)學(xué)推理過程中最頻繁應(yīng)用到的泛函,在計(jì)算機(jī)語言中卻沒有對(duì)應(yīng)的表達(dá),換言之?dāng)?shù)學(xué)思維不能很自然地表述為計(jì)算機(jī)語言的話,那么計(jì)算機(jī)對(duì)于數(shù)學(xué)研究的意義就顯得很可疑了,畢竟那時(shí)候的計(jì)算機(jī)可不是用來玩大菠蘿3的。所以這里就有了兩撥人,務(wù)實(shí)的一撥人開發(fā)出了 fortran ,力主解決數(shù)值計(jì)算;務(wù)虛的一撥人則創(chuàng)造了 lisp ,試圖一舉解決符號(hào)計(jì)算的難題。在 John McCarthy 所作的 history of lisp 中這樣寫到:?
Then mathematical neatness became a goal and led to pruning some features from the core of the language.(保證數(shù)學(xué)上的簡(jiǎn)潔性成為我們的目標(biāo),并因此拒絕了將一些特性加入到語言核心中。)?
This was partly motivated by esthetic reasons and partly by the belief that it would be easier to devise techniques for proving programs correct if the semantics were compact and without exceptions.(這部分是基于美學(xué)上的考慮,部分是因?yàn)槲覀兿嘈?#xff0c;緊湊而沒有特例的語法才更有可能設(shè)計(jì)出一種從數(shù)學(xué)上證明程序正確的方法。)?
之所以講了這么多關(guān)于數(shù)學(xué)和歷史的東西,是因?yàn)槲矣X得在看這本書前,最重要的是理解: lisp 是什么。而我又一直相信理解一樣事物最好的辦法就是理解其歷史。(順帶說一句,以上歷史都是在看過書以后才找的,所以也是我的血淚教訓(xùn)……)如上所示, lisp 是一門為了表達(dá)數(shù)學(xué)推導(dǎo)過程而誕生的語言,所以不可避免地使得 SICP 前兩章的例子幾乎全是數(shù)學(xué)問題。代碼只是其形,而其神是純粹的數(shù)學(xué)。所以這里似乎就陷入了一種兩難的境地:如果執(zhí)意于寫代碼的話,那看起來做的都是形而下的工作;而如果只思考問題的數(shù)學(xué)原理的話,那姑且不說是舍本逐末,至少也是偏離主題了。在看完 SICP 以后我始終懷著這種疑問而不解——看的時(shí)候是不會(huì)有這種感覺的,因?yàn)樽⒁饬θ考m結(jié)于書中的題目了——不過在寫這篇書評(píng)時(shí)又翻了一下第一章,似乎明白了。
小節(jié)1.1寫到:?
一個(gè)強(qiáng)有力的程序設(shè)計(jì)語言,不僅是一種指揮計(jì)算機(jī)執(zhí)行任務(wù)的方式,它還應(yīng)該成為一種框架,使我們能夠在其中組織自己有關(guān)計(jì)算過程的思想。每一種強(qiáng)有力的語言都為此提供了三種機(jī)制:基本表達(dá)形式,組合的方法,抽象的方法。
所以我認(rèn)為 SICP 這本書最主要的目的,就是“教你用 lisp 的語言,來組織,來抽象,來表達(dá)想法”。從這個(gè)意義上來說, SICP 和一本 Learning Python ,或者一本 C Programming Language 并沒有太多的區(qū)別,依然講授的是“用特定的語言求解特定的問題”。不過略有不同的是, lisp 太特殊了,導(dǎo)致從 c 轉(zhuǎn)向 python 或許不需要太多的思維轉(zhuǎn)換,但從 c 轉(zhuǎn)向 lisp 卻需要對(duì)思維習(xí)慣大改造一番,這我想就是 SICP 地位如此之高的原因吧。我也同意,學(xué)習(xí) SICP 確實(shí)很鍛煉思維,以及培養(yǎng)一種更加高度的抽象習(xí)慣。其實(shí)在看 SICP 的過程中,很多時(shí)候我都會(huì)感慨,“如果我不是數(shù)學(xué)系的,這一段到底會(huì)怎么理解呢”。一個(gè)典型例子就是習(xí)題2.6,初看我也一頭霧水,后來才意識(shí)到 zero 是 f->id 的泛函,正是零映射, one 是 f->f 的泛函,正是恒同映射,也就是函數(shù)空間的1。如果沒有學(xué)過泛函分析的我來看這道題目,估計(jì)只能好不容易推導(dǎo)出規(guī)律后,感慨于 Church 計(jì)數(shù)的“巧妙”了。所以從好的層面來看, SICP 至少能夠帶來泛函的直觀感受,因此我才說 SICP 是一本寫給CS人的泛函數(shù)。但是從壞的層面上說,數(shù)學(xué)抽象畢竟是象牙塔里的產(chǎn)物,當(dāng)好不容易抽象出一個(gè)優(yōu)雅的模型卻發(fā)現(xiàn)手頭的語言難以表達(dá)或者效率上有種種顧慮的時(shí)候,還是很郁悶的吧。
前面貌似說了 SICP 的不少壞話,其實(shí)只是想拉低一下 SICP 的評(píng)價(jià),至少使得后人不至于期待過高。SICP 是一本好書,至少是一本有趣的書,這點(diǎn)我是非常贊同的。就沖著她那創(chuàng)意的封面圖和作者頭像,每章開篇都會(huì)引用一段(非常利于裝逼)的名言,以及用半頁的篇幅講述和主題完全無關(guān)的 MIT 第一任校長(zhǎng)的生平,想不有趣都難啊。不過我還是世俗一下,列一下自己看過這本書以后比較"現(xiàn)實(shí)"的收獲:?
關(guān)于習(xí)題
最后說一下習(xí)題,習(xí)題的重要性想來大家都很清楚。
本書共有5章,每章都有近100道習(xí)題。這本書可以說是時(shí)間黑洞。每章分為4-5節(jié),每節(jié)有幾個(gè)小節(jié),全書有一百小節(jié)(即X.X.X)左右。我以小節(jié)為單位進(jìn)行了估算,包括完成習(xí)題,每小節(jié)大約需要一個(gè)小時(shí)。當(dāng)然不同小節(jié)難度不同,有的耗時(shí)長(zhǎng)些,有的短些。于是讀完本書并做完大部分習(xí)題需要上百個(gè)小時(shí)。再加上聽課或看視頻教程的時(shí)間則會(huì)更長(zhǎng)。所以我覺得恐怕只有在校學(xué)生才有時(shí)間和精力來完成這本書的學(xué)習(xí)。
不過對(duì)于 SICP 來說,我覺得習(xí)題未必都要寫成代碼,在紙上寫出思路和關(guān)鍵代碼也未為不可。因?yàn)?scheme 的編碼效率實(shí)在不高(就是 scheme 逼得我給 vim 裝上 surround 插件……),而習(xí)題重要的還是整個(gè)抽象的過程。另外就是,要找個(gè)好一點(diǎn)的解釋器,我下了 MIT 的 scheme 解釋器發(fā)現(xiàn)各種操作太不人性了……?
為什么推薦SICP?
向大家推薦 SICP,不知道有多少人看了,也不知道有多少人明白了,更不知道有多少人驚嘆了。或者你根本不屑一顧,或者你看見 Lisp 那層層括號(hào)心生畏懼,又或者你了了一瞥,覺得沒什么精彩之處。那我真的很失望。
我為什么要推薦SICP,而且為什么如此執(zhí)著?這本不算厚的書帶給我的觀念,是從未有過的,是關(guān)乎于軟件本質(zhì)的。曾幾何時(shí),我覺得我看到了計(jì)算機(jī)編程書中沒有的哲學(xué)觀,但這一次我的夢(mèng)破滅了,那些已經(jīng)被寫進(jìn)書里差不多快 30 年了。
我現(xiàn)在就來談?wù)勎业男牡?#xff0c;以再次向你展現(xiàn)這本書的魔力。
第一章作為基礎(chǔ),作者并沒有象后續(xù)章節(jié)寫太多的軟件思想,主要還是介紹 Scheme 語言,所以草草看去,沒什么精辟之處。不過在第一章中,作者用了大量的篇幅來探討數(shù)學(xué)問題,因?yàn)樗胂蚰憬沂境绦蛟O(shè)計(jì)中的核心哲學(xué):抽象。而數(shù)學(xué)無疑是最好的例子。
了解數(shù)學(xué)史的人,應(yīng)該知道整個(gè)數(shù)學(xué)史,就是一個(gè)不斷抽象的歷史。古希臘人將字母引入計(jì)算,使數(shù)學(xué)不再只是算術(shù),而且具有表達(dá)抽象規(guī)則的能力。近代數(shù)學(xué)對(duì)函數(shù)和微積分的探求中,用 f(x) 替代了多項(xiàng)式表達(dá)式,函數(shù)更一般了,然后 n 維空間、復(fù)分析、映射、泛函,抽象代數(shù)、群論,等等等等,直到集合論,摧毀了數(shù)學(xué)的基石,使數(shù)學(xué)界再次陷入沉思。
構(gòu)造程序的方法也是抽象。從最簡(jiǎn)單的元素開始,基本元素(自演算表達(dá)式,包括數(shù)字,字符串和布爾值),然后定義基本過程(基本運(yùn)算符,四則運(yùn)算和布爾運(yùn)算),進(jìn)一步,自定義標(biāo)識(shí)符(如同代數(shù)),再自定義過程(函數(shù)),再將過程作為值參與運(yùn)算(高階過程)。一步步的抽象,形成了整個(gè)程序的結(jié)構(gòu)。而我們編程,無非就是從現(xiàn)實(shí)世界抽象出模型,再將模型不斷的提煉抽象,屬性、方法、類、繼承、層次、框架。
編程就是一個(gè)不斷抽象的過程。我再次把作者在第一章末寫下的結(jié)論抄在這里,作為最后的注腳。
“作為編程者,我們應(yīng)該對(duì)這類可能性保持高度敏感,設(shè)法從中設(shè)別出程序中的基本抽象,基于它們?nèi)ミM(jìn)一步構(gòu)造,并推廣它們以創(chuàng)建威力更強(qiáng)大的抽象。當(dāng)然,這并不是說總應(yīng)該采用盡可能抽象的方式去寫程序,程序設(shè)計(jì)專家們知道如何根據(jù)工作中的情況,去選擇合適的抽象層次。但是,能基于這種抽象去思考確實(shí)是最重要的,只有這樣才可能在新的上下文中去應(yīng)用它們。高階過程的重要性,就在于我們能顯式地用程序設(shè)計(jì)語言的要素去描述這些抽象,使我們能像操作其他計(jì)算元素一樣去操作它們。”?
?
============?End
?
總結(jié)
以上是生活随笔為你收集整理的关于《计算机程序的构造和解释》的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言常用数据类型所占用的字节数
- 下一篇: centos7安装redmine3.4