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