LISP定義
LISP是一種通用高級(jí)計(jì)算機(jī)程序語言,長期以來壟斷人工智能領(lǐng)域的應(yīng)用。LISP作為因應(yīng)人工智能而設(shè)計(jì)的語言,是第一個(gè)聲明式系內(nèi)函數(shù)式程序設(shè)計(jì)語言,有別于命令式系內(nèi)過程式的C、Fortran和面向?qū)ο蟮?a href="http://www.delux-kingway.cn/v/tag/852/" target="_blank">Java、C#等結(jié)構(gòu)化程序設(shè)計(jì)語言。
Lisp是一門歷史悠久的語言,全名叫LISt Processor,也就是“表處理語言”,它是由John McCarthy于1958年就開始設(shè)計(jì)的一門語言。和Lisp同時(shí)期甚至更晚出現(xiàn)的許多語言如Algo等如今大 多已經(jīng)消亡,又或者僅僅在一些特定的場合有一些微不足道的用途,到現(xiàn)在還廣為人知的恐怕只剩下了 Fortran和COBOL。但唯獨(dú)Lisp,不但沒有隨著時(shí)間而衰退,反倒是一次又一次的煥發(fā)出了青春,從Lisp分支出來的Scheme、ML等語言在很多場合的火爆程度甚至超過了許多老牌明星。那么這顆常青樹 永葆青春的奧秘究竟在哪里呢?
如果你只接觸過C/C++、Pascal這些“過程式語言”的話,Lisp可能會(huì)讓你覺得十分不同尋常,首先吸引你眼球(或者說讓你覺得混亂的)一定是Lisp程序中異常多的括號(hào),當(dāng)然從現(xiàn)在的角度來講,這種設(shè)計(jì)的確對(duì)程序員不大友好,不過考慮到五六十年代的計(jì)算機(jī)處理能力,簡化語言本身的設(shè)計(jì)在那時(shí)算得上是當(dāng)務(wù)之急了。
Lisp的基本語法很簡單,它甚至沒有保留字(有些語言學(xué)家可能對(duì)這一點(diǎn)有異議),它 只有兩種基本的數(shù)據(jù),僅有一種基本的語法結(jié)構(gòu)就是表達(dá)式,而這些表達(dá)式同時(shí)也就是程序結(jié)構(gòu),但是正如規(guī)則最簡單的圍棋卻有著最為復(fù)雜的變化一樣,Lisp使用最基本的語言結(jié)構(gòu)定義卻可以完成其它語言難于實(shí)現(xiàn)的、最復(fù)雜的功能。
語言應(yīng)用
LISP是函數(shù)式程序設(shè)計(jì)的先鋒,其諸多革命性的創(chuàng)新思維影響了后續(xù)編程語言的發(fā)展,亦完全壟斷人工智能領(lǐng)域的應(yīng)用長達(dá)三分之一個(gè)世紀(jì)。曾在開展初年出現(xiàn)的低效率因素亦在集體改良中被移去,成就了現(xiàn)在廣被應(yīng)用于軟件開發(fā)、電子商務(wù)及金融系統(tǒng)的Common Lisp、Scheme、Emacs Lisp和Clojure等。
?
關(guān)于Lisp9種語言思想
Lisp語言誕生的時(shí)候就包含了9種新思想。其中一些我們今天已經(jīng)習(xí)以為常,另一些則剛剛在其他高級(jí)語言中出現(xiàn),至今還有2種是Lisp獨(dú)有的。按照被大眾接受的程度,這9種思想依次如下排列。
(1) 條件結(jié)構(gòu)(即if-then-else結(jié)構(gòu))?,F(xiàn)在大家都覺得這是理所當(dāng)然的,但是Fortran I就沒有這個(gè)結(jié)構(gòu),它只有基于底層機(jī)器指令的goto結(jié)構(gòu)。
(2) 函數(shù)也是一種數(shù)據(jù)類型。在Lisp語言中,函數(shù)與整數(shù)或字符串一樣,也屬于數(shù)據(jù)類型的一種。它有自己的字面表示形式(literal representation),能夠存儲(chǔ)在變量中,也能當(dāng)作參數(shù)傳遞。一種數(shù)據(jù)類型應(yīng)該有的功能,它都有。
(3) 遞歸。Lisp是第一種支持遞歸函數(shù)的高級(jí)語言。
(4) 變量的動(dòng)態(tài)類型。在Lisp語言中,所有變量實(shí)際上都是指針,所指向的值有類型之分,而變量本身沒有。復(fù)制變量就相當(dāng)于復(fù)制指針,而不是復(fù)制它們指向的數(shù)據(jù)。
(5) 垃圾回收機(jī)制。
(6) 程序由表達(dá)式組成。Lisp程序是一些表達(dá)式樹的集合,每個(gè)表達(dá)式都返回一個(gè)值。這與Fortran和大多數(shù)后來的語言都截然不同,它們的程序由表達(dá)式和語句組成。
區(qū)分表達(dá)式和語句在Fortran I中是很自然的,因?yàn)樗恢С终Z句嵌套。所以,如果你需要用數(shù)學(xué)式子計(jì)算一個(gè)值,那就只有用表達(dá)式返回這個(gè)值,沒有其他語法結(jié)構(gòu)可用,否則就無法處理這個(gè)值。
后來,新的編程語言支持塊結(jié)構(gòu),這種限制當(dāng)然也就不存在了。但是為時(shí)已晚,表達(dá)式和語句的區(qū)分已經(jīng)根深蒂固。它從Fortran擴(kuò)散到Algol語言,接著又?jǐn)U散到它們兩者的后繼語言。
(7) 符號(hào)類型。符號(hào)實(shí)際上是一種指針,指向存儲(chǔ)在散列表中的字符串。所以,比較兩個(gè)符號(hào)是否相等,只要看它們的指針是否一樣就行了,不用逐個(gè)字符地比較。
(8) 代碼使用符號(hào)和常量組成的樹形表示法。
(9) 無論什么時(shí)候,整個(gè)語言都是可用的。Lisp并不真正區(qū)分讀取期、編譯期和運(yùn)行期。你可以在讀取期編譯或運(yùn)行代碼,也可以在編譯期讀取或運(yùn)行代碼,還可以在運(yùn)行期讀取或者編譯代碼。
在讀取期運(yùn)行代碼,使得用戶可以重新調(diào)整(reprogram)Lisp的語法;在編譯期運(yùn)行代碼,則是Lisp宏的工作基礎(chǔ);在運(yùn)行期編譯代碼,使得Lisp可以在Emacs這樣的程序中充當(dāng)擴(kuò)展語言(extension language);在運(yùn)行期讀取代碼,使得程序之間可以用S表達(dá)式(S-expression)通信,近來XML格式的出現(xiàn)使得這個(gè)概念被重新“發(fā)明”出來了。
Lisp語言剛出現(xiàn)的時(shí)候,這些思想與其他編程語言大相徑庭,后者的設(shè)計(jì)思想主要由50年代后期的硬件決定。隨著時(shí)間流逝,流行的編程語言不斷更新?lián)Q代,語言設(shè)計(jì)思想逐漸向Lisp靠攏。思想(1)到思想(5)已經(jīng)被廣泛接受,思想(6)開始在主流編程語言中出現(xiàn),思想(7)在Python語言中有所實(shí)現(xiàn),不過似乎沒有專用的語法。
思想(8)可能是最有意思的一點(diǎn)。它與思想(9)只是由于偶然原因才成為Lisp語言的一部分,因?yàn)樗鼈儾粚儆邴溈ㄥa的原始構(gòu)想,是由拉塞爾自行添加的。它們從此使得Lisp語言看上去很古怪,但也成為了這種語言最獨(dú)一無二的特點(diǎn)。說Lisp語言古怪倒不是因?yàn)樗恼Z法很古怪,而是因?yàn)樗緵]有語法,程序直接以解析樹(parse tree)的形式表達(dá)出來。在其他語言中,這種形式只是經(jīng)過解析在后臺(tái)產(chǎn)生,但是Lisp直接采用它作為表達(dá)形式。它由列表構(gòu)成,而列表則是Lisp的基本數(shù)據(jù)結(jié)構(gòu)。
用一門語言自己的數(shù)據(jù)結(jié)構(gòu)來表達(dá)該語言是非常強(qiáng)大的功能。思想(8)和思想(9),意味著你可以寫出一種能夠自己編程的程序。這可能聽起來很怪異,但是對(duì)于Lisp語言卻是再普通不過。最常用的做法就是使用宏。
術(shù)語“宏”在Lisp語言中的意思與其他語言中的不一樣。Lisp宏無所不包,它既可能是某樣表達(dá)式的縮略形式,也可能是一種新語言的編譯器。無論是想真正理解Lisp語言,還是只想拓寬編程視野,最好都學(xué)學(xué)宏。就我所知,宏(采用Lisp語言的定義)目前仍然是Lisp獨(dú)有的。一個(gè)原因是為了使用宏,你大概不得不讓你的語言看上去像Lisp一樣古怪。另一個(gè)可能的原因是,如果你想為自己的語言添上這種終極武器,你從此就不能聲稱自己發(fā)明了新語言,只能說發(fā)明了一種Lisp的新方言。
我把這件事當(dāng)作笑話說出來,但是事實(shí)就是如此。如果你創(chuàng)造了一種新語言,其中有car、cdr、cons、quote、cond、atom、eq這樣的功能,還有一種把函數(shù)寫成列表的表示方法,那么在它們的基礎(chǔ)上完全可以推導(dǎo)出Lisp語言的所有其他部分。事實(shí)上,Lisp語言就是這樣定義的,麥卡錫把語言設(shè)計(jì)成這個(gè)樣子就是為了讓這種推導(dǎo)成為可能
#e#
為什么Lisp沒有流行起來
很久以前,這種語言站在計(jì)算機(jī)科學(xué)研究的前沿,特別是人工智能的研究方面?,F(xiàn)在,它很少被用到,這一切并不是因?yàn)楣爬希愃乒爬系恼Z言卻被廣泛應(yīng)用。
其他類似的古老的語言有 FORTRAN、 COBOL、 LISP、 BASIC、 和ALGOL 家族,這些語言的唯一不同之處在于,他們?yōu)檎l設(shè)計(jì)。FORTRAN是為科學(xué)家和工程師設(shè)計(jì)的,他們?cè)谟?jì)算機(jī)上編程的目的是是為了解決問題。COBOL是為了商業(yè)設(shè)計(jì)的,最好的體現(xiàn)在于讓商人們可以利用電腦時(shí)代。LISP是了計(jì)算機(jī)科學(xué)研究設(shè)計(jì)的,最突出的體現(xiàn)在計(jì)算機(jī)基本原理研究。。BASIC是為初學(xué)者設(shè)計(jì)的。最后,ALGOL語言是有計(jì)算機(jī)程序員修改,演變成其他流行的語言,如C,Pascal和Java的一個(gè)龐大的家族。
上面提到的某些語言已經(jīng)不像當(dāng)初那么流行了。我們?cè)谶@里可以把它們稱作“失敗”。問題是它們?yōu)槭裁词??第一站出來的是COBOL. 很不幸,它以面向商業(yè)人員的很好的可讀性就是它的失敗點(diǎn)。商業(yè)人員發(fā)現(xiàn),他們可以雇傭程序員去管理他們的系統(tǒng)。程序員自然會(huì)偏向于為他們?cè)O(shè)計(jì)的語言,而不是他們的老板。所以隨著時(shí)間推移,越來越多的商業(yè)功能都使用例如VB, C, C++ 和JAVA實(shí)現(xiàn)了。現(xiàn)在,只有很少一部分軟件仍通過COBOL語言編寫。
BASIC卻有不同的命運(yùn)。他是為入門人員設(shè)計(jì)的。那些在微機(jī)上學(xué)習(xí)編程,他們會(huì)使用內(nèi)置的BASIC語言作為起點(diǎn)。隨著時(shí)間推移,微機(jī)被運(yùn)行微軟操作系統(tǒng)的個(gè)人電腦,或者M(jìn)acOS的蘋果電腦所代替。這種語言逐漸被VB所取代。雖然他是面向初級(jí)程序員,它有一段時(shí)間代替了COBOL。為什么要耗費(fèi)這么多的資源在昂貴的編譯器上,而便宜的解釋器在我們的電腦上已經(jīng)存在?最近,微軟以遷移到.NET框架上,讓VB跟在后面。它的替代者, C#就是ALGOL家族中的一員,跟Java相近。
這些年FORTRAN的使用起起伏伏。在某一階段,差不多所有科學(xué)方面的代碼是用它來寫的。它的優(yōu)點(diǎn)是這門語言中沒有指針,并且不允許存在遞歸。這意味著所有數(shù)據(jù)的引用位置都可以在編譯時(shí)確定。FORTRAN編譯器利用這些額外的信息使程序運(yùn)行格外地迅速。不幸的是,隨著時(shí)間的推移,固定大小的數(shù)組這種數(shù)據(jù)結(jié)構(gòu)變得過時(shí)了。現(xiàn)在,科學(xué)要處理任意形狀的風(fēng)格,甚至表述更為復(fù)雜的真實(shí)世界。這需要在語言中額外地加入指針。這些情況發(fā)生的時(shí)間段里,F(xiàn)ORTRAN逐漸走向沒落。現(xiàn)在,它被轉(zhuǎn)移到高性能計(jì)算工作,其中新的并行矩陣和矢量運(yùn)算最近添加到這門語言中,仍然使它擁有性能優(yōu)勢(shì)。
ALGOL語言家族取得了成功。其原因是,這些語言是由程序員為程序員寫的。隨著時(shí)間的推移,這些與系統(tǒng)和應(yīng)用相關(guān)的語言成為了現(xiàn)在最常用的語言。它的優(yōu)點(diǎn)是越多地程序員使用,這門語言就能得到更多地改進(jìn),并且越來越多地程序是用它們來寫就的。這提供了一個(gè)良性循環(huán),更多的程序員們又被聘請(qǐng)?jiān)诩壕帉懙某绦蛏瞎ぷ?。這是一個(gè)網(wǎng)絡(luò)效應(yīng)的例子。一個(gè)系統(tǒng)的“價(jià)值”是它的用戶數(shù)目的平方,在于以此速率增長的用戶之間的交互作用。
那么為什么Lisp語言家族會(huì)站在失敗者一邊呢?有些人認(rèn)為是語法的錯(cuò)。Lisp因?yàn)樗睦ㄌ?hào)而臭名昭著。我并不認(rèn)為是這個(gè)理由。許多用戶說良好的格式可以讓他們跟上這些括號(hào)。同時(shí),Lisp語言被發(fā)明不久后,有一個(gè)叫“super-bracket”的語法可以讓人快速表示出任意數(shù)量的回括號(hào)”)”。這個(gè)特性在今天已經(jīng)很少有人使用了。最后,優(yōu)秀的編輯器解決了大多數(shù)的語法問題。
另一些人經(jīng)常抱怨Lisp是一門函數(shù)式語言。這是失敗的理由嗎?自然,跟早期的語言相比,只有Lisp算是函數(shù)式的。但事實(shí)上,我認(rèn)為沒有這么簡單。Lisp也有命令式語言的特性,ALGOL系列語言也可以被當(dāng)作一門純正的函數(shù)式語言來用。 如果有人想選擇一種特定的編程范式來寫代碼,一些特定的語言可以讓這個(gè)選擇更容易的實(shí)現(xiàn)。然而,現(xiàn)代語言已經(jīng)足夠靈活,它們能支持多種編程范式,近乎完全命令式的Lisp沒有理由不存在。
或許lisp的問題在于他使用了垃圾回收?在那個(gè)時(shí)候,只有l(wèi)isp作為計(jì)算機(jī)語言采用了這個(gè)特性。誠然,垃圾回收會(huì)占用大量的計(jì)算資源,而早期計(jì)算機(jī)在該方面的不足足以組織lisp大展拳腳了。但是,我認(rèn)為這仍然不是主要的原因。lisp是用來寫那些復(fù)雜度相當(dāng)高的程序的,而這些程序在事實(shí)上都必須帶有一個(gè)垃圾回收模塊,如果你用其他的語言來寫……大概很難比lisp實(shí)現(xiàn)的要好吧?眾所周知的事實(shí)是,任何一個(gè)如此復(fù)雜的程序,如果用其他語言寫的話都不可避免的戴上一個(gè)比lisp垃圾回收臃腫不少的功能模塊……
lisp的失敗,恰恰是因?yàn)樗晒?,這讓他的目標(biāo)變得模糊。lisp相對(duì)與早期的語言實(shí)在是非常靈活,靈活到足以改變自身形式以適應(yīng)需求。對(duì)于其他的語言來說,如果想要完成一個(gè)龐大的任務(wù),就需要把這個(gè)任務(wù)打碎成一小塊一小塊的然后完成。如果是一個(gè)更大的呢?甚至連編譯都需要分步完成了。但是lisp不是這樣的,由于他強(qiáng)大的能力,程序員可以將lisp改造成特定領(lǐng)域的專門工具——順手的工具將順手的解決問題——任務(wù)輕松完成了。由于語言的正交性(譯者注:這里可能應(yīng)該理解為“自洽”),我們改造過的lisp仍然可以使用原有的編譯器,解釋器運(yùn)行。
那么建立特定領(lǐng)域的語言來作為一個(gè)問題的解決方案,它會(huì)出現(xiàn)什么問題呢?結(jié)果是它非常高效。然而,這種做法會(huì)使語言分化。這導(dǎo)致許多子語言都略有不同。這是Lisp代碼對(duì)其他人而言可讀性差的真正原因。在其他語言中,相對(duì)來說比較簡單就能臆測(cè)出一段給定代碼的作用。有著超強(qiáng)的表達(dá)力的Lisp,由于一個(gè)給定的符號(hào)(symbol)可能是一個(gè)變量,函數(shù)或操作,需要閱讀大量代碼才能找出它。
Lisp失敗的原因是因?yàn)樗乃槠⑶宜乃槠且驗(yàn)槠湔Z言天性與特定領(lǐng)域方案的風(fēng)格造成的。而網(wǎng)絡(luò)效應(yīng)則恰恰相反。越來越少的程序員使用相同的方言,因此它相對(duì)與ALGOL語言家族的總價(jià)值下降。
如果有人現(xiàn)在設(shè)計(jì)一種語言,該如何避免這種問題呢?如果語言的表達(dá)性是我們的目標(biāo),那么它必須以某種方式加以調(diào)整。這門語言必須要有特意的限制,來保證所編寫代碼的可讀性。Python是一門成功的語言,它已經(jīng)做到了這些,其中某些限制是硬編碼的,而另一些則是以約定成俗的方式存在。
不幸的是,這么久過去了并且發(fā)明了這么多Lisp的變種語言,在其之上建立的其它新語言大概并不是所要的答案。根本不會(huì)有足夠多的用戶使它與眾不同。也許解決的辦法是,慢慢加入類似Lisp的語言功能到ALGOL語言家族中。幸運(yùn)的是,這似乎是正在發(fā)生的事。新的語言(C#,D,Python等)趨向于擁有垃圾回收機(jī)制。他們也往往比舊的語言更具正交性。在未來,最終會(huì)有行為很像Lisp的一種流行語言。
評(píng)論