1. 程式人生 > >如何成為 Lisp 程式設計師的

如何成為 Lisp 程式設計師的

我成為 Lisp 程式設計師的道路曲折而漫長。我曾於 2007 年 10 月 3 日在自己的日記中總結了自己的學習經歷,現抄錄於此。

最早在 2000 年 5 月,斯托曼院士訪華時告訴我,Lisp (或者它的現代變種 Scheme)是功能最強大的程式語言,他本人就是一位高階的 Lisp 程式設計師,他還精通 C,GNU Emacs 就是採用 C 和 Lisp 兩者開發的。我當時已經掌握了 C,但不會用 Lisp,但是我完全相信他說的都是真的。於是,一心想成為程式設計高手的我,決定學習和掌握這門程式語言。我從 2000 年下半年開始學習 Lisp。下面總結的學習經歷大致按照時間先後順序排出:

有必要先說明一點,在我遇到斯托曼院士之前,我在上大學時曾經閱讀過一本關於人工智慧的著作,《Artificial Intelligence --- Making machines "think"》,作者是 Neill Graham,這本書的最後一章(第14章)是關於 Lisp 語言的簡介,所以我對人工智慧和 Lisp 的概念並不完全陌生。這本書前面的章節都很好理解,但是在這最後一章,我遇到了很大困難。我花了許多時間試圖明白其中的道理,不過最 後成效不彰,現在回想起來,究其緣故,最主要的就是我沒有一個適當的上機練習環境,在讀了許多書中的東西后,當時我感覺似乎明白了其 道理,但是實際上並沒有真正理解清楚。不過,有兩點在我看來無疑是確定的:一、Lisp 早已經成為人工智慧研究專案的首選(或者說是預設的)程式設計工具,在人工智慧領域沒有其他語言能撼動其領導地位。二、對於具有表結構的 資料操作,對於列表(list)頭元素(pair 的 car 的部分)的處理採用遞迴方式比較好,而對於眾多的體元素(pair 的 cdr 的部分)則採用迭代的方式處理效率更好。

斯托曼院士回國後,我首先在計算機上嘗試用 Emacs Lisp 程式設計,它是嵌入在 GNU Emacs 文字編輯器中的直譯器。在龐大的 Lisp 家譜中,Emacs Lisp 不是 Common Lisp,而是早期的 MacLisp 的一個直系後代,同時在一些方面作了簡化和強化。同時我開始閱讀 Robert Chassell 所著 《Introduction to Emacs Lisp Programming》,Robert Chassell 是斯托曼院士早年結識的戰友,也是自由軟體基金會的合創人之一, 他很早就使用 GNU Emacs,而且使用 Emacs Lisp 程式定製 GNU Emacs,斯托曼友善地把 Robert Chassell 介紹給我認識。 這本書既是自由文件(可以從 GNU 的網站自由下載),又是自由軟體基金會出版社(GNU Press)的出版物。等我讀完了這本書之後,我覺得這本書實在太美妙了,作者的文筆十分了不起(即使對於想學習英文寫作的人,幫助 也應該很大),把這本書介紹給其他人是完全值得的。我於是找了兩位翻譯人員(毛文濤博士和呂芳女士),把它譯成了中文,我則擔任了全 書的編輯和審校工作。中文版質量很高,我很滿意,它作為一本很偉大的程式設計入門書籍十分適合廣大讀者自學(我認為讀者應該搞到一本閱 讀)。我至今還想自己動手翻譯這本書的第三版,可惜如今我很難再找到當年那麼多的時間做編輯和審校之類的工作了。

閱讀完這本書之後,我意識到如果想使用 Emacs Lisp 開發非玩具級別的實際應用程式,那麼根據作者的推薦,自由軟體基金會出版的 《GNU Emacs Lisp Reference Manual》是必不可少的工具書,我列印了這份文件的第 2.4 版本,厚厚的共四本。後來這份文件正式出版,從 GNU 網站上訂購的圖書升級到了 2.6 版本,針對的是 GNU Emacs version 21。我不太認同 Eric Raymond 在他的名著 《The Art of Unix Programming》中對 Emacs Lisp 的評論,他以為 Emacs Lisp 只能為 Emacs 編輯器本身編寫控制程式,而趕不上其他指令碼語言全面。實際上,我認為只要熟悉了 Emacs Lisp 的細節,其他任何指令碼語言能完成的工作,都可以使用 Emacs Lisp 程式完成。我親眼看見斯托曼院士在 GNU Emacs 內完成電子郵件的編輯、收發等工作,不用 Eric Raymond 開發的 fetchmail 程式一樣乾得很好。我自己也利用 Emacs Lisp 編寫過 CGI 應用程式,效果也不錯。

Bob Glickstein 曾經寫過一本《Writing GNU Emacs Extensions》,可以配合 Robert Chassell 的書與《GNU Emacs Lisp Reference Manual》,作為補充讀物。

讀了 Robert Chassell 的書之後,我開始花時間閱讀 David Touretzky 博士所著的 《Common Lisp: A Gentle Introduction to Symbolic Computation》,這本書可以從網際網路上自由下載,讀者可以自行在 全球資訊網上 google 得到它。 這也是一本偉大的 Lisp 著作,內容已經是基於 Common Lisp 的,但是作者並沒有特意強調這一點。我把下載的 PDF 檔案打印出來,自己動手把打印出的文件紙張裝訂成了兩卷手冊。  我從這本書中得到的最大收穫是我充分認識到 Lisp 中的一切都是物件:數字原子(numeric atoms)和符號原子(symbolic atoms)都是物件。數字原子求值返回它自身的值,而符號原子則有名稱(name)、型別(type)、值(value)、秉性表 (plist)和繫結表(bindlist)。這五個欄位可以放入一個數據結構中,並在實現中以 C 語言的 struct 表達。

在閱讀這些材料的同時,我又從網上找到了 Gary Knott 教授編寫的一份文件,《Interpreting Lisp》,這份文件篇幅不長,從來沒有正式出版成書。在這份文件中,作者利用 C 語言編寫了一個微小的 Lisp 實現,非常接近於最初的 Lisp 實現。最可貴的是他將實現的原始碼和盤托出。從這本書中, 我清晰地看到了如何構造 Lisp 物件的結構,我開始認識到記憶體垃圾收集演算法的重要性。在理解了 David Touretzky 博士所著的 《Common Lisp: A Gentle Introduction to Symbolic Computation》介紹的 Lisp 物件的結構基礎上,我明白了書中圖示的 Lisp 物件中若僅在結構設計時安排五個欄位是不夠的,還需要有供垃圾回收(GC,Garbage CCollector)模組操作的欄位才行。

在 2001-2002 期間,我開始接觸 Scheme。在此之前的 2000 年 8 月,Rorbert Chassell 曾來中國訪問,我們在西安時,他向我介紹了 Scheme 是我應該關注的語言。Scheme 於 1985 年誕生於 MIT,發明人有兩位:一位是 Gerald Sussman 教授,他是自由軟體基金會的董事會成員之一,另一位是 Guy J.Steele 博士,下面即將更多地提到。我首先使用了 Dorai Sitaram 所著的教材《Teaching Yourself Scheme in Fixnum Days》,這份文件可以從網路自由下載,不過坦率地說,這本書教材不太適合初學者,閱讀它的人至少應該具有很多基礎知識或者經驗才 行。我並沒有從這本書從獲得太多的幫助,對 Scheme 的基本概念也沒有搞清楚,特別是連續(Continuation)之類的概念沒有理解。不過,收穫還是有很多,我意識到 Scheme 是一個非常優美的 Lisp 變種,它繼承了源自 Algol60 和早期 Lisp 兩者的特點。這份教材的最後還列舉出了一個具體的程式設計例項,講授如何利用 Scheme 編寫 CGI 程式。現在我看來,在 CGI 程式設計等領域, Perl 和 PHP 等指令碼語言獲得廣泛應用簡直就是鑽了 Lisp 社團不善營銷的空子。

到了 2001 夏天,我從美國獲得了《Structure and Interpretation of Computer Programs》的教師手冊,是我的朋友 James Gray 幫我買的,他是我一個很好的朋友,不過我這裡要善意地埋怨一下,他做事有些馬大哈,我原來希望他給我搞到這本書的學生用書,沒有想到 他卻寄來了教師手冊,我想他在買書時沒有仔細區分一下,而此著作的教師手冊和學生用書的封面都採用了同一圖案,作者和 MIT 出版社的編輯的確很優秀,他們在營銷本書時非常成功,後來專門還開設了一個網站推廣本書和相關的教學材料,網上公佈了教材的全部內 容,加上兩位作者課堂教學的視訊錄影。 教師手冊價格比較便宜,James 在購買時要麼沒有仔細辨認清楚,要麼就是幫我省錢,才寄來了這本教師手冊。我當時沒有學生手冊,也就沒有繼續學習 Scheme。不過話說回來,James 寄給我的教師手冊我一直都留在我手頭,後來對於我在黑客道教學中講授 Scheme 幫助極大,在這裡還是應該深深地感謝 James Gray。

不久(2003年),我買了一本 Patrick Winston 教授所著的《Common Lisp》第三版, 作者是麻省理工學院人工智慧實驗室的主任(斯托曼早年就是他手下的兵),在美國的人工智慧研究領域名氣很大,我就是衝著他的名氣才買 此書的。閱讀完之後,我覺得這位教授名副其實,而不像我在國內見到的一些人徒有虛名,拿不出真東西。 這是一本介紹 Common Lisp 的極好教材。後來 Hans Hagen (ConTeXt 排版軟體包的主要作者之一)告訴我這本書的合著者  Berthold Klaus Paul Horn 在 TeX 社團名氣也很大,的確如此,從本書的排版質量即可看出許多名堂來,排版樣式一看就是典型的 TeX 風格。 作為中文 TeX 使用者俱樂部(CTUG)的主席,我已經知道,國內學術界(我指的是數學界和計算機科學技術界)很少有人精通 TeX 排版系統,鮮有人能使用它排版自己的講義或著作。

讀了這本書之後,我感覺自己必須閱讀 Common Lisp 的語言參考手冊,許多問題必須在看到語言規範(這是基本的尺度)之後才能搞清楚。 Guy L. Steele 博士寫過這樣的手冊,而且他寫了兩次,第一個版本是在 Common Lisp 標準化之前完成的,第二個版本是在標準化完成之後寫成的,但是網上有人評論說有些該寫的東西沒有寫進去,此書是否會有第三版不得而 知。Guy L. Steele 博士是非常著名的語言手冊的作者,他已經為 C、Common Lisp、Java 等程式語言都寫過的語言參考手冊,都非常成功,這些參考手冊都是一版再版,銷路極好,比位於瑞士的國家標準化組織(ISO)釋出的非 常昂貴的標準文件銷路好許多。他寫的這些語言參考手冊已經成為編寫這些語言編譯器作者們的大救星。

大約在同時,我下載了 《On Lisp》,這是 Paul Graham 博士編寫的一本優秀著作,從中我得到了許多 Lisp 概念的細節,特別是 Lisp 的 macro 機制,以及黑客們如何利用 Lisp 思考問題。作者介紹的自底向上(bottom up)的方法論對我觸動很大,而作者的講解是非常富於啟發性的(作者曾專程赴義大利的美術學院學習過油畫創作,所以具有很高的藝術修 養)。從那時開始,混合程式設計(Hybrid Programming)的思想在我頭腦中開始成型,我堅信 Lisp 將會成為一種非常長壽的程式語言,這使我聯想起斯托曼院士當年在四川九寨溝就 GNU Emacs 開發對我講過的話。在 GNU Emacs 和 Lisp 背後隱含的方法論是永遠不會過時的。

2004 年,我真正找到了 Lisp 程式設計的感覺,覺得自己開始進入狀態,並開始使用 Scheme 開發真正的應用程式,我編寫的程式是一個網路應用程式,即一個網路留言板(Web-based bulletin System),在全球資訊網上可以執行,CGI 的模組是採用 Scheme 寫的,Apache 在伺服器上通過 Scheme 的 CGI 程式接上了 PostgreSQL 資料庫。我使用的是 PLT Scheme 的 103 版本,我非常喜歡這個版本,既簡單又很乾淨,我用 C 語言和 PostgreSQL 提供的 libpg 編寫了一個 DA (database adaptor),讓 Scheme 程式可以訪問 PostgreSQL 資料庫。

完成了這個專案之後,好事成雙,我得到了渴望已久的《Structure and Interpretation of Computer Programs》(簡稱 SICP,或者“紫皮書”),作者就是 Harold Abelson 教授和 Gerald Sussman 教授。正是這一年,我開始利用自己頭腦中形成的數學觀點,特別是在我的泛系尺度論中表達的思想,來認真學習 Scheme,並且主動地從中國古代的陰陽太極圖模型來理解當今電子計算機系統上的計算模型。這一過程延續了很長時間,直到 2005 年的冬天才最終獲得成功! 這期間的許多思想寫入了我的著作《自由軟體:新的遊戲規則》第三卷內篇的第二章“論尺度”。今後我還準備花更多的時間把它擴充套件開來, 形成一部單行本的著作《泛系尺度論》,在這個單行本中,我將利用更長的篇幅把中國古代的哲理思想、現代數學思想和計算機程式設計融為一 體,對整個計算理論提出自己完整的一家之言。在黑客道九個段位中,初段就是講“計算的本質”,裡面就納入了我的思想方法和程式設計經驗。

2004-2005 年期間,我仔細地研究了 R5RS 文件中除了第七章之外的所有內容,收穫巨大。對於第七章的內容,當時仍然有些疑惑,因為這些材料需要理解大量的關於 lambda calculi 的細節和大量的預備知識,我當時還沒有找到充分的材料鑽研。另外,在研究 PLT Scheme 的原始碼時,記憶體垃圾回收演算法對我而言,仍然是一大疑難問題,顯然,對於記憶體垃圾回收技術,我還需要學習更多的背景材料。

到了 2005 年的年底,我把 R5RS 翻譯成了中文。在完成翻譯的過程中,我知道了如何利用形式語言和擴充套件的巴科斯-勞爾正規化(EBNF)來定義一門程式語言的形式句法和 語義規則,以及如何正確理解和讀懂它。

2005-2006期間,我學習了其他許多關於 Lisp 程式設計的書籍,包括 Paul Graham 博士的《ANSI Common Lisp》、 Matthew Flatt 等人合著的 《How to Design Programs》, Brian Harvey 和 Matthew Wright 合著的 《Simply Scheme --- Introducing Computer Science》(此書的封面設計別出心裁,非常值得回味)、Daniel Friedmann 和 Matthias Felleisen 合著的 《The Little Schemer》 和《The Seasoned Schemer》。另外我花了相當多的時間仔細閱讀 《The Scheme Programming Language, 3e》,這是 R. Dybvig 教授的代表作,他是 Chez Scheme 實現的設計大師。年底我得到了 《Hackers and Painters》(“黑客和畫家”),這是 Paul Graham 博士所著的散文集,與 Robert Chassell 一樣,他也是一位偉大的作家,他的行文非常容易閱讀,而且這本書中的內容如同其書名副標題一樣,的確收入了許多偉大的想法,這些想法 對於創新公司利用 Lisp 開發創新專案是非常富有啟發性的。

2006 年 7 月 15 日,我的學生千俊哲從南韓的漢城大學帶來了他學習的兩本著作的影印件:George Springer 和 Daniel P. Friedman 合著的《Scheme and the Art of Programming》,以及 Mark Watson 的 《Programming in Scheme: Learn Scheme through Artificial Intelligence Programs》。前一本的難度在紫皮書之下,比較好讀,其中許多程式如同棋譜一樣,展現了許多高階程式設計技巧,值得反覆思考,我立 即在 PLT Scheme 實現上驗證了書中的大部分程式碼;後一本則介紹如何使用 MIT Scheme 來設計人工智慧程式,非常精彩。

2006 年 7 月送走了千俊哲之後的夏天,我一直在瑞士蘇黎世度假(八月下旬我還去了西班牙馬德里參加了國際數學家大會),蘇黎世中央圖書館 (ZB,Zentralbibliothek Zuerich)是一所了不起的圖書館,現有藏書一百二十萬種。據說列寧當年在歐洲流亡時曾來到蘇黎世,就睡在這個圖書館裡讀書學 習。八月份時,大多數瑞士人也在休假,圖書館的人不多,非常安靜。我在這段時間從圖書館中找到了非常多的背景材料, 包括 H.P. Barendregt 所著的數學經典教材 《The Lambda Calculus --- Its Syntax and Semantics》,這本書於 1981 年由 North Holland 出版社出版,對這一數學分支作了詳盡的介紹,我認為對於這一主題, 今後再也無人可以寫得比這本著作更好了。 另外,我找到了第一本關於 lambda calculus 的著作,是由這個理論的創始人 Alonzo Church 教授創作的,《The Calculi of Lambda-conversion》 簡直是無價之寶,在這本小冊子中,作為數學家,作者清晰而精煉地闡明瞭 lambda calculi 的全部內容。任何一位想掌握 lambda calculus 的人都應該仔細閱讀本書。在圖書館中還找到了 《An Introduction to Lmabda Calculi for Computer Scientists》, 作者是 Chris Hankin。 Matthias Felleisen 和 Matthew Flatt 合寫的 《Programming Language and Lambda Calculi》也打印出來了,並仔細閱讀了兩遍,這兩人是 PLT Scheme 研發小組的核心成員。 在蘇黎世中央圖書館的書架上,我還看到了 SCIP 紫皮書的第一版的德文字,書中的內容與英文版第二版大同小異,但是我敏銳地發現,第一版的第四章中沒有收入 eval 和 apply 兩個高階運算元構成的太極推手圖,第二版中則出現了。

我花時間研究了 《Lisp 1.5 Programmer's Manual》,這是世界上第一份真正意義上正式釋出過的 Lisp 穩定實現版本的手冊,作者就是 John McCarthy 等人,極具學術權威性,我認為任何一位 Lisp 程式設計師都應該閱讀這本手冊。時隔多年後,我又開始閱讀關於人工智慧方面的著作,《Lisp, Lore and Logic》是 W. Richard Stark 寫的,《Artificial Intelligence, theory and practice》 是 Thomas Dean 等人寫的,他們都已經使用 Common Lisp 來說明問題。閱讀時,我參考了前面已經提到的 Partrick Winston 教授編寫的經典教材 《Artificial Intelligence, 3e》。

2007 年初,我開始關注 Scheme 社團中尚處於起草狀態中的 R6RS,這個檔案將成為新的 Scheme 語言規範。我現在仍然認為 Common Lisp 太複雜、太龐大,回廠大修似乎也不太可能,因為工業界已經很好地接受了 Common Lisp,而 Scheme 將是未來的主流。我開始按照這一規範來開發自己的 Scheme 實現版本,這一實現版本稱為 MNM Scheme。

2007年6月至7月間,我在瑞士蘇黎世的 Campus Zollikerberg 列印了 R5.97RS,我花了許多時間理解這一新的規範,特別是它與前一個版本(R5RS)的差異。同時,我重新思考了 PLT Scheme 實現的原始碼和涉及模組(modules)、名稱空間(namespaces)、盒子(box)型別、define-values 和其他附加在 R5RS 規範之上的特性與實現風格。

從蘇黎世中央圖書館借到的另外一本具有重大價值的著作就是 Richard Jones 和 Rafael Lins 合著的《Garbage Collection》,這本書極大地幫助我理解了記憶體垃圾回收演算法設計的細節。我從此開始真正明白了 Scheme 實現工作中的最後一個陰暗角落。對於一切 Lisp 物件,記憶體垃圾收集的演算法設計時其實不存在理論上的最優演算法,演算法的效率受到多種因素的影響,而 Lisp 的設計者可以根據自己的設計思想來決定應該怎樣回收記憶體垃圾。

這時的我已經成長為熟練的 C++ 程式設計師,站在 C++ 程式設計師的立場看,一切 Lisp 物件都有型別,我可以用 C++ 語言內建的類(class)來刻畫它們(即宣告各種使用者自定義的類),一切 Lisp 物件從儲存空間分配和回收的角度來看具有共性,所以,這可以利用 C++ 的模板來表達 Lisp 物件的儲存管理結構,而各個 Lisp 物件佔有的儲存空間大小,則可以利用類的建構函式(constructor)和解構函式(destructor)對記憶體分配和記憶體垃 圾回收在模板的支援下進行統一的操作。

源自 Lisp 發展起來的 GC 是一項“古老”的技術,實際上它已經廣泛地被採用了,Java 這門當今新的商業程式語言中就有,而且 Java 的 GC 演算法設計得非常好,我決定在我的 Scheme 實現中參考它。2007 年 9 月,我在從香港飛往蘇黎世的飛機上,我閱讀了美國著名的程式設計師 Bruce Eckel 所著的 《Thinking in Java》的第四版原著,從作者的介紹中,我結合從《Garbage Collection》中獲得的知識,我理解了 Java 的記憶體垃圾回收演算法的總體思路,並構思瞭如何利用他們的演算法來改進我的設計。 而在我離開蘇黎世回國的同一天(2007年9月26日),R6RS 技術委員會的全體編輯成員決定凍結對草案的討論,正式釋出了這一規範,從那一天起,我實現 MNM Scheme 的步伐也大大加快了。(不過,請讀者留意在委員會舉辦的投票時,也有許多人投了反對票,並給出了反對意見,這些意見與支援的意見一 起,都是極有研究價值的。)

Scheme 的實現版本非常多,而且自己能否動手實現一個是考驗一個計算機專業人士學術修養深淺的好指標(這也是我在黑客道中把第五段的教學內 容 定為“直譯器的原理與構造”的原因之一)。迄今優秀的 Scheme 實現有: PLT Scheme、MIT Scheme、Chez Scheme 等等。我認為 PLT Scheme 是非常優秀的實現版本,它是按照 GPL 釋出的自由軟體,值得在這裡推薦給廣大讀者。毫不客氣地講,我的 MNM Scheme 也應該算一個。

Common Lisp 的實現版本很多,拋開 Franz Lisp 等商業版本不談,自由軟體社團中最有名的兩個實現分別是:Bruno Haible 等人從 1992 年以來一直維護和開發的 CLISP,以及卡耐基梅隆大學的小組開發 CMU Common Lisp。這兩個 Common Lisp 實現都很好,我個人比較喜歡使用 CLISP。目前最好的 Common Lisp 程式設計著作可推薦 Peter Seibel 寫的 《Practical Common Lisp》,這位作者的天分顯然比我高,他原來是 Java 和 Perl 程式設計師,2004年才開始學習 Common Lisp,他花了一年的時間學習它,就完全學會了,而且在學習的同時,邊練習、邊寫書,結果很豐碩,寫出的這本書就是讀者能看到的結 果,這本書得到了廣泛的認可,它出版後獲得了美國出版界計算機圖書創作的震撼大獎。他在一次採訪中提出了一個新的說法:時代的發展需 要“第二代 Lisp 程式設計師”,而且每個程式設計師都應該學習 Lisp。(讓我再次回憶起斯托曼院士在 2000 年時對我說過的話。)

正如讀者在上面所讀到的,成為一位瞭解 Lisp 一切內幕的程式設計師可真不容易,但是我很高興,因為我已經成功地逾越過了這個初看起來曾經非常高的門檻,我現在已經是這樣一位程式設計師 了。學習 Lisp 給我帶來了巨大的樂趣,如果沒有這種在程式設計中產生的樂趣相伴,我絕對不會花這麼長的時間來學習它。今天,我由衷地自豪,因為如果按照 Peter Seibel 的說法和衡量標準,我已經是第二代優秀的 Lisp 程式設計師群體中的一分子。

上面羅列的學習經歷對於一般人而言顯然太長了,黑客道學員們則可以站在我的肩膀上,借鑑我的經驗和教訓,少走許多彎路、避免走死胡 同。值得慶幸的是,我已經在黑客道的課程設計中自覺地做了許多工作,凡是參加了黑客道的初段課程學習的學員(S1:“計算的本 質”),即可在較短的時間內學會掌握 Lisp。