計算機是個特別有意思的學科,在普通人看來計算機專業(yè)大都是“修電腦的”,在同行看來,我們都是Neo。其實我一直都不知道這個專業(yè)到底是干什么的,這個問題我不知道答案,也不想知道了,別人怎么看我不在乎,我只不過是在追逐自己喜歡的東西罷了。不過就計算機這個學科而言,這個由數(shù)學家、物理學家和黑客湊在一起組成的學科,它的那些事是真的很有意思,且聽我慢慢道來。
我覺得無論什么學科,學習其相關(guān)知識,其歷史非常重要,歷史代表著知識的整體面貌,從歷史中我們能得到很多重要的東西,知識不是突然出現(xiàn)然后就立刻被人們接受的,而是,通過時間的沉淀一點點積累完善出來的,這也是為什么在很多國家,歷史是一門必修課。但是,從來沒有人或書系統(tǒng)地講述過計算機科學的歷史,一切好像突然就出現(xiàn)了的樣子,這樣,由于不知道現(xiàn)在的知識是從何而來,一切都很難理解和接受,只能強迫自己接受,而強迫自己接受的知識跟本稱不上理解,更不要提更上一層樓了,舉幾個例子:
- 關(guān)于設計模式。許多人捧著GoF的《Design Patterns》視其為圣經(jīng),不過Lisp程序員會告訴你,設計模式在很大程度上是為了彌補程序語言本身的缺陷,《黑客與畫家》里提到過: Peter Norvig發(fā)現(xiàn),總共23中設計模式之中,有16中在Lisp語言中“本聲就提供,或者被大大簡化”。退一步講,設計模式是先輩在無數(shù)實踐當中總結(jié)出的經(jīng)驗,很多地方并不需要設計模式,卻被人強加上用來展現(xiàn)自己的水平,沒有必要。
- 關(guān)于程序語言。許多人都是:“聽說學習這門語言對以后找工作有幫助”才去學習,目的功利。他們想知道學習什么語言才好,于是聽從了身邊或者網(wǎng)上的“大神”的意見,從不懷疑,“大神”的言論成了教條或者信仰。有人反駁,那我到底聽誰的呢?要我說,還是要聽自己的,保持懷疑的態(tài)度,別人說他好或者壞,我都質(zhì)疑,自己默默去學了,自然就能做出客觀的判了?;钤谧汾s別人的道路上最終只會淪為歷史的小丑而已。當你學的語言夠多,縱觀程序語言史, 知道了原來程序語言大都學習了其它的語言,語言相像相通,自然就有了自己的選擇。就像武林高手,你天天練習高手用的招式也達不到人家的水平,原因在于武林高手習得的武功無數(shù),你所聽到的只是武林高手的殺手锏罷了,人家對武功已有深刻而獨到的見解,而這些你是學不到的。
只有縱觀歷史,才能知道學什么。有關(guān)CS的歷史,要從18世紀的一個人開始講起,他的名字叫Leibniz,大家都知道他是個數(shù)學家,因創(chuàng)造微積分而聞名,其實Leibniz是個全才,他曾提出一個宏偉的想法:
- Create a ‘universal language’ in which all possible problems can be stated.
建立一門“通用語言”,以便表述所有的問題。 - Find a decision method to solve all the problems stated in the universal language.
找到一種判定方法,以便解決所有上面用通用語言陳述的問題。
經(jīng)過Frege和Russell等數(shù)學家的努力,想法一從數(shù)學角度已經(jīng)可以用類似集合論的理論用一階邏輯來表述。于是后來想法二成了一個重要的哲學問題:“人能解決所有用通用語言表述的問題嗎?”,答案似乎是否定地,但又不知道如何證明,這就是著名的Entscheidungsproblem(德語“判定問題”)。1936年,Alonzo Church和Alan Turing各自獨立的給出了判定問題的否定答案。為了解釋此問題,就需要“判定”,或者說“可計算”的正規(guī)定義,于是他倆分別給出了各自的計算模型:
- Church發(fā)明了一套叫做lambda演算的系統(tǒng),并借此系統(tǒng)定義了可計算函數(shù)的概念。
- Turing發(fā)明了一種機器(后來被叫做圖靈機)并借此機器定義了可計算函數(shù)的概念。
同年,Turing又證明了兩種模型是一樣的健壯,因為他們定義了相同的可計算函數(shù)。后來基于圖靈機的概念,出現(xiàn)了馮諾伊曼計算機,其本質(zhì)就是有著隨機訪問寄存器的圖靈機。命令式語言,如Fortran、Pascal,還有匯編語言,都是基于圖靈機的構(gòu)造:命令。而像Miranda,ML等函數(shù)式語言則是基于lambda演算,較早的例子則有Lisp語言。再后來,幾乎所有的程序語言,所有的編程概念、方法都是從這兩個模型出發(fā)而得到的。
Lisp和C是最能代表這兩個計算模型的語言,Lisp和C代表著兩個極端,前者——Lisp是函數(shù)式語言的鼻祖,是第二古老的程序語言,至今活躍著,特別是在人工智能方面的應用(因為McCarthy也是人工智能概念的鼻祖,共享一個爹);后者——C就更不用說了,TIOBE程序語言排行中C能排第一全靠上個世紀人們寫的海量C代碼,UNIX、WINDOWS和Linux都是C編寫的(至于UNIX為什么用C寫,還是因為共享一個爹)??吹竭@里你肯定會奇怪:怎么沒有人用Lisp來寫操作系統(tǒng)呢。不是沒有,基于Lisp語言的計算機叫Lisp機,后來被歷史淘汰,其原因有很多:用來實現(xiàn)Lisp機的語言,叫做Lisp Machine Lisp,采用的是動態(tài)作用域;當時Lisp的編譯器都實現(xiàn)的不好,很慢;馮構(gòu)架的電腦已經(jīng)成為主流,而此構(gòu)架不是Lisp這種動態(tài)類型語言能發(fā)揮實力的地方...??傊?,歷史已經(jīng)成為歷史,現(xiàn)在的Lisp方言,Scheme和Common Lisp等才是Lisp的未來。Stallman當年想振興Lisp于是搞了個GNU計劃,寫了個操作系統(tǒng)底層用C,頂層邏輯用Lisp,但是自己設計的內(nèi)核太難實現(xiàn),bug多還難調(diào)試,看到網(wǎng)上論壇有個叫Linus的家伙寫了個內(nèi)核,好用又穩(wěn)定,于是項目組有人就提議就用Linus的內(nèi)核得了,再后來就莫名其妙成了Linux,Stallmal臉一黑,哪個亂起名字,明明GNU才是操作系統(tǒng),怎么Linux就風光無限了,再后來Stallman一看,既然Linux搞得如火如荼,那就繼續(xù)搞抄襲UNIX吧,反正大家不知道,于是把原本振興Lisp的想法就置于身后了,歷史就是這么巧。你要是想體驗下Lisp機,那用Emacs吧,GUN的Emacs編輯器基本就是個Lisp機實現(xiàn)的樣子了。
還有件事不得不提,其實,Church是Turing的博導,可以說,Turing最大的貢獻,就是把Church晦澀難懂的邏輯學以通俗易懂的方式展示出來,簡單易懂的命令式語言,讓除了邏輯學專家以外的普通人也能編程,命令式風格漸漸深入人心,成為主流。不過隨著程序語言的發(fā)展,Python、Ruby等類函數(shù)式語言也漸漸被人們所接受和喜愛,不僅因為人們對函數(shù)式語言的誤解慢慢解開(最大的誤解要數(shù):函數(shù)式語言不能賦值),還因為hacker們的技術(shù)越來越高,對更簡潔更短小代碼的追求促使他們走向抽象能力更強的函數(shù)式語言了。后來出現(xiàn)的面向?qū)ο蟾拍詈艽蟪潭壬弦仓皇浅橄蟮囊环N方式,其背后并沒有計算模型作為理論基礎,Lisp中就有著名的CLOS為Lisp提供面向?qū)ο笾С?,這就是Lisp的優(yōu)勢之一:在語言上構(gòu)建語言,永遠不會過時。所謂的程序語言之爭,歸根結(jié)底就是那個有更好的抽象能力。不過更高的抽象,意味著更難理解,這也就是為什么"There's Only One Way To Do It"的Python、“Batteries Included”的Python會這么受歡迎。用著別人寫的庫,寫個爬蟲只要幾十行,當然簡單了,但是覺得自己很厲害就是你的不對了。
程序語言就如同棋一樣,什么象棋,圍棋,五子棋,各自有各自的規(guī)則,各自有各自的棋子和棋盤,這些游戲的樂趣就在于規(guī)則,各種不同的組合,根據(jù)不同的情況能有千變?nèi)f化的結(jié)果。拿象棋來說,中國象棋最初的目的是戰(zhàn)爭推演,其實就是古代的實戰(zhàn)沙盤。每種不同的棋子,代表的是一種兵種,不過,象棋只是打仗而已,不是最精妙的,最精妙的還要數(shù)圍棋,如同《天才在左瘋子在右》里所說:
- “圍棋代表的是真正的智慧!圍棋可以說是社會的濃縮,我不能理解圍棋是怎么發(fā)明的,所以民間對于圍棋的起源,有很大的傳說性質(zhì)。你想象一下,各19條平行線交叉,361個點,黑白一共360個棋子,沒有高低貴賤之分,完全依靠操縱者的智慧?;蛘呗涫志d綿,或者落手鏗鏘,或者匪夷所思,或者殺聲四起。你以為天下在握的時候,突然四面楚歌,生死難卜啊。這是什么?不就是社會嗎?依靠的是什么?一個規(guī)則,一個簡單的規(guī)則,棋子呢?就是人。大家都是一樣的狀態(tài)。但是落點決定了你的與眾不同,而且每一個都是與眾不同!”
圍棋,多么完美的抽象啊,那程序語言中的圍棋是什么呢,我覺得就是Lisp了。如果你學過Lisp,那你一定會被它特別的S表達式驚艷到,所有的程序都由S表達式組成,連數(shù)據(jù)也不需要xml這種外部DSL來存儲,而都用S表達式組成,程序也可以看作數(shù)據(jù),(xml文法就是S表達式),既然數(shù)據(jù)可以修改,那可看作數(shù)據(jù)的程序也自然可以修改了,這里的修改并不是你寫代碼的時候改來改去的修改,而是程序編譯或者運行時,自己修改自己,怎么修改呢?由S表達式組成的程序就像森林,修改程序就是修改樹上的結(jié)點,這就是宏的工作了。
當你介紹你的朋友給其他人時,會介紹那些重要、理所當然的東西,比如姓名啊,性格啊設想,如果你要介紹一門程序語言給其他人,你會介紹些什么呢?語法?變量類型?這些當然不可或缺,語法就像下棋的規(guī)則,類型就像棋子的分類,但這還不夠抽象,你學棋不是為了學習規(guī)則和棋子的,而是為了下棋,在下棋上進行抽象才是更深的抽象。對應到程序語言上,就是這三點:
- 原始數(shù)據(jù)類型。
- 組合數(shù)據(jù)的方法。
- 抽象的方法。
都說書籍是人類進步的階梯,一點都沒錯。你要是想學C,書多的都不知道選那一本好了,相比之下,學Lisp就不像學C那么養(yǎng)尊處優(yōu)了,不過,反過來看,正因為書少,那些都是質(zhì)量超高的、由真正懂計算機的人寫的書:
- 《計算機程序的構(gòu)造與解釋》:著名的小紫本,國外很多大學CS專業(yè)的必修課的課本,編程即為抽象,數(shù)據(jù)即為過程,也只有用Lisp(本書用的其方言Scheme)才能講的明白吧,后來授課語言改為Python,課本和課程已經(jīng)完全變了,真是殘念。如果你想知道什么是編程、編程的本質(zhì),小紫本必不可少。如果這本書讀起來比較晦澀難懂,那先看HTDP(程序設計方法)是個不錯的選擇。
- 《黑客與畫家》:不是程序員也看看這本書吧。不只是對創(chuàng)業(yè)者和“黑客”,更是對那些普通讀者,如果你想理解我們所處的計算機時代,那這本書會告訴你計算機的過去、現(xiàn)在和未來。
- 《On Lisp》和《Let Over Lambda》:Lisp美在S表達式,Lisp厲害在它的宏。在語言上設計語言,用代碼生成代碼,自底向上程序設計,交互式開發(fā),在代碼層進行抽象,閉包代表數(shù)據(jù),閉包即為過程,等等一切,Lisp告訴你抽象的力量。