你不可不知的9種Lisp語言思想

turingbooks發表於2011-11-08

本文來源

 Lisp語言誕生的時候就包含了9種新思想。其中一些我們今天已經習以為常,另一些則剛剛在其他高階語言中出現,至今還有2種是Lisp獨有的。按照被大眾接受的程度,這9種思想依次如下排列。

(1) 條件結構(即if-then-else結構)。現在大家都覺得這是理所當然的,但是Fortran I就沒有這個結構,它只有基於底層機器指令的goto結構。

(2) 函式也是一種資料型別。在Lisp語言中,函式與整數或字串一樣,也屬於資料型別的一種。它有自己的字面表示形式(literal representation),能夠儲存在變數中,也能當作引數傳遞。一種資料型別應該有的功能,它都有。

(3) 遞迴。Lisp是第一種支援遞迴函式的高階語言。

(4) 變數的動態型別。在Lisp語言中,所有變數實際上都是指標,所指向的值有型別之分,而變數本身沒有。複製變數就相當於複製指標,而不是複製它們指向的資料。

(5) 垃圾回收機制。

(6) 程式由表示式組成。Lisp程式是一些表示式樹的集合,每個表示式都返回一個值。這與Fortran和大多數後來的語言都截然不同,它們的程式由表示式和語句組成。

區分表示式和語句在Fortran I中是很自然的,因為它不支援語句巢狀。所以,如果你需要用數學式子計算一個值,那就只有用表示式返回這個值,沒有其他語法結構可用,否則就無法處理這個值。

後來,新的程式語言支援塊結構,這種限制當然也就不存在了。但是為時已晚,表示式和語句的區分已經根深蒂固。它從Fortran擴散到Algol語言,接著又擴散到它們兩者的後繼語言。

(7) 符號型別。符號實際上是一種指標,指向儲存在雜湊表中的字串。所以,比較兩個符號是否相等,只要看它們的指標是否一樣就行了,不用逐個字元地比較。

(8) 程式碼使用符號和常量組成的樹形表示法。

(9) 無論什麼時候,整個語言都是可用的。Lisp並不真正區分讀取期、編譯期和執行期。你可以在讀取期編譯或執行程式碼,也可以在編譯期讀取或執行程式碼,還可以在執行期讀取或者編譯程式碼。

在讀取期執行程式碼,使得使用者可以重新調整(reprogram)Lisp的語法;在編譯期執行程式碼,則是Lisp巨集的工作基礎;在執行期編譯程式碼,使得Lisp可以在Emacs這樣的程式中充當擴充套件語言(extension language);在執行期讀取程式碼,使得程式之間可以用S表示式(S-expression)通訊,近來XML格式的出現使得這個概念被重新“發明” 出來了。

Lisp語言剛出現的時候,這些思想與其他程式語言大相徑庭,後者的設計思想主要由50年代後期的硬體決定。隨著時間流逝,流行的程式語言不斷更新換代,語言設計思想逐漸向Lisp靠攏。思想(1)到思想(5)已經被廣泛接受,思想(6)開始在主流程式語言中出現,思想(7)在Python語言中有所實現,不過似乎沒有專用的語法。

思想(8)可能是最有意思的一點。它與思想(9)只是由於偶然原因才成為Lisp語言的一部分,因為它們不屬於麥卡錫的原始構想,是由拉塞爾自行新增的。它們從此使得Lisp語言看上去很古怪,但也成為了這種語言最獨一無二的特點。說Lisp語言古怪倒不是因為它的語法很古怪,而是因為它根本沒有語法,程式直接以解析樹(parse tree)的形式表達出來。在其他語言中,這種形式只是經過解析在後臺產生,但是Lisp直接採用它作為表達形式。它由列表構成,而列表則是Lisp的基本資料結構。

用一門語言自己的資料結構來表達該語言是非常強大的功能。思想(8)和思想(9),意味著你可以寫出一種能夠自己程式設計的程式。這可能聽起來很怪異,但是對於Lisp語言卻是再普通不過。最常用的做法就是使用巨集。

術語“巨集”在Lisp語言中的意思與其他語言中的不一樣。Lisp巨集無所不包,它既可能是某樣表示式的縮略形式,也可能是一種新語言的編譯器。無論是想真正理解Lisp語言,還是隻想拓寬程式設計視野,最好都學學巨集。就我所知,巨集(採用Lisp語言的定義)目前仍然是Lisp獨有的。一個原因是為了使用巨集,你大概不得不讓你的語言看上去像Lisp一樣古怪。另一個可能的原因是,如果你想為自己的語言添上這種終極武器,你從此就不能聲稱自己發明了新語言,只能說發明了一種Lisp的新方言。

我把這件事當作笑話說出來,但是事實就是如此。如果你創造了一種新語言,其中有car、cdr、cons、quote、cond、atom、eq這樣的功能,還有一種把函式寫成列表的表示方法,那麼在它們的基礎上完全可以推匯出Lisp語言的所有其他部分。事實上,Lisp語言就是這樣定義的,麥卡錫把語言設計成這個樣子就是為了讓這種推導成為可能。

 

相關文章