不久前,如果你問 Lisp 是用來干什么的,很多人會(huì)回答說 "人工智能(articial intelligence)" 。事實(shí)上,Lisp 和人工智能之間的聯(lián)系只是歷史的偶然。 Lisp 由 John McCarthy 發(fā)明,同樣是他首次提出了 "人工智能" 這一名詞。那時(shí)他的學(xué)生和同事用 Lisp 寫程序,于是它就被稱作一種 AI 語言。這個(gè)典故在 1980 年代 AI 短暫升溫時(shí)又被多次提起,到現(xiàn)在已經(jīng)差不多成了習(xí)慣。
幸運(yùn)的是, "AI 并非 Lisp 的全部" 的觀點(diǎn)已經(jīng)開始為人們所了解。近年來軟硬件的長(zhǎng)足發(fā)展已經(jīng)讓 Lisp 走出了象牙塔:
它目前用于GNUEmacs -- Unix 下最好的文本編輯器;
AutoCAD -- 工業(yè)標(biāo)準(zhǔn)的桌面CAD 程序;
還有Interleaf -- 領(lǐng)先的高端出版系統(tǒng)。
Lisp 在這些程序里的應(yīng)用跟AI 已經(jīng)沒有了任何關(guān)系。
如果 Lisp 不是一種 AI 語言,那它是什么?與其根據(jù)那些使用它的公司來判斷 Lisp ,我們不如直接看看語言本身。有什么是你可以用 Lisp 做到,而其他語言沒法做到的呢? Lisp 的一個(gè)最顯著的優(yōu)點(diǎn)是可以對(duì)其量身定制,讓它與用它寫的程序相配合。Lisp 本身就是一個(gè) Lisp 程序,Lisp 程序可以表達(dá)成列表,那也是 Lisp 的數(shù)據(jù)結(jié)構(gòu)。
總之,這兩個(gè)原則意味著任何用戶都可以為 Lisp 增加新的操作符,而這些新成員和那些內(nèi)置的操作符是沒有區(qū)別的。
由于 Lisp 賦予了你自定義操作符的自由,因而你得以隨心所欲地將它塑造成你所需要的語言。
如果你在寫一個(gè)文本編輯器,那么可以把 Lisp 轉(zhuǎn)換成專門寫文本編輯器的語言。
如果你在編寫 CAD 程序,那么可以把 Lisp 轉(zhuǎn)換成專用于寫 CAD 程序的語言。
并且如果你還不太清楚你要寫哪種程序,那么用 Lisp 來寫會(huì)比較安全。
因?yàn)闊o論你想寫哪種程序,在你寫的時(shí)候,Lisp 都可以演變成用于寫那種程序的語言。
你還沒想好要寫哪種程序?一樣可以。對(duì)有些人來說,這種說法有點(diǎn)不對(duì)勁。這和某種行事方式很不一樣,這種方式有兩步:
(1) 仔細(xì)計(jì)劃你打算做的事情,接下來
(2) 去執(zhí)行它。
按照這個(gè)邏輯,如果 Lisp 鼓勵(lì)你在決定程序應(yīng)該如何工作之前就開始寫程序,它只不過是慫恿你匆忙上馬,草率決定而已。
事實(shí)并非如此。先計(jì)劃再實(shí)施的方法可能是建造水壩或者發(fā)起戰(zhàn)役的方式,但經(jīng)驗(yàn)并未表明這種方法也適用于寫程序。為什么?也許是因?yàn)橛?jì)算機(jī)的要求太苛刻了。也許是因?yàn)槌绦蛑械淖償?shù)比水壩或者戰(zhàn)役更多。或許老方法不再奏效的原因,是因?yàn)榕f式的冗余觀念不適用于軟件開發(fā):
如果一座大壩澆鑄了額外的 30% 的混凝土,那是為以后的誤操作留下的裕量,但如果一個(gè)程序多做了額外 30% 的工作,那就是一個(gè)錯(cuò)誤。
很難說清原來的辦法為什么會(huì)失效,但所有人都心知肚明老辦法不再行之有效。究竟有幾次軟件按時(shí)交付過?有經(jīng)驗(yàn)的程序員知道無論你多小心地計(jì)劃一個(gè)程序,當(dāng)你著手寫它的時(shí)候,之前制定的計(jì)劃在某些地方就會(huì)變得不夠完美。有時(shí)計(jì)劃甚至?xí)e(cuò)得無可救藥。卻很少有"先策劃再實(shí)施" 這一方法的受害者站出來質(zhì)疑它的有效性。相反,他們把這都?xì)w咎于人為過失:
只要計(jì)劃做的更周詳,所有的問題就都可以避免。
就算是最杰出的程序員,在進(jìn)行具體實(shí)現(xiàn)的時(shí)候也難免陷入麻煩,因此要人們必須具備那種程度的前瞻性可能過于苛求了。也許這種先策劃再實(shí)施的方法可以用另外一種更適合我們自身限制的方法取而代之。
如果有合適的工具,我們完全可以換一種角度看待編程。為什么我們要在具體實(shí)現(xiàn)之前計(jì)劃好一切呢?盲目啟動(dòng)一個(gè)項(xiàng)目的最大危險(xiǎn)是我們可能不小心就使自己陷入困境。但如果存在一種更加靈活的語言,是否能為我們分憂呢?我們可以,而且確實(shí)如此。Lisp 的靈活性帶來了全新的編程方式。在 Lisp 中,可以邊寫程序邊做計(jì)劃。
為什么要等事后諸葛亮呢?正如 Montaigne 所發(fā)現(xiàn)的那樣,如果要理清自己的思路,試著把它寫下來會(huì)是最好的辦法。一旦你能把自己從陷入困境的危險(xiǎn)中解脫出來,那你就可以完全駕馭這種可能性。邊設(shè)計(jì)邊施工有兩個(gè)重要的后果:程序可以花更少的時(shí)間去寫,因?yàn)楫?dāng)你把計(jì)劃和實(shí)際動(dòng)手寫放在一起的時(shí)候,你總可以把精力集中在一個(gè)實(shí)際的程序上;然后讓它變得日益完善,因?yàn)樽罱K的設(shè)計(jì)必定是進(jìn)化的成果。
只要在把握你程序的命運(yùn)時(shí)堅(jiān)持一個(gè)原則:一旦定位錯(cuò)誤的地方,就立即重寫它,那么最終的產(chǎn)品將會(huì)比事先你花幾個(gè)星期的時(shí)間精心設(shè)計(jì)的結(jié)果更加優(yōu)雅。
Lisp 的適應(yīng)能力使這種編程思想成為可能。確實(shí),Lisp 的最大危險(xiǎn)是它可能會(huì)把你寵壞了。使用 Lisp 一段時(shí)間后,你會(huì)開始對(duì)語言和應(yīng)用程序之間的結(jié)合變得敏感,當(dāng)你回過頭去使用另一種語言時(shí),總會(huì)有這樣的感覺:
它無法提供你所需要的靈活性。
有一條編程原則由來已久:作為程序的功能性單元不宜過于臃腫。如果程序里某些組件的規(guī)模增長(zhǎng)超過了它可讀的程度,它就會(huì)成為一團(tuán)亂麻,藏匿其中的錯(cuò)誤就好像巨型城市里的逃犯那樣難以捉摸。這樣的軟件將難以閱讀,難以測(cè)試,調(diào)試起來也會(huì)痛苦不堪。
按照這個(gè)原則,大型程序必須細(xì)分成小塊,并且程序的規(guī)模越大就應(yīng)該分得越細(xì)。但你怎樣劃分一個(gè)程序呢?傳統(tǒng)的觀點(diǎn)被稱為自頂向下的設(shè)計(jì):你說 "這個(gè)程序的目的是完成這七件事,那么我就把它分成七個(gè)主要的子例程。第一個(gè)子例程要做這四件事,所以它將進(jìn)一步細(xì)分成它自己的四個(gè)子例程",如此這般。這一過程持續(xù)到整個(gè)程序被細(xì)分到合適的粒度 每一部分都足夠大可以做一些實(shí)際的事情,但也足夠小到可以作為一個(gè)基本單元來理解。
有經(jīng)驗(yàn)的 Lisp 程序員用另一種不同的方式來細(xì)化他們的程序。和自頂向下的設(shè)計(jì)方法類似,他們遵循一種叫做自底向上的設(shè)計(jì)原則, 即通過改變語言來適應(yīng)程序。在 Lisp 中,你不僅是根據(jù)語言向下編寫程序,也可以根據(jù)程序向上構(gòu)造語言。在編程的時(shí)候你可能會(huì)想 " Lisp 要是有這樣或者那樣的操作符就好了。" 那你就可以直接去實(shí)現(xiàn)它。之后,你會(huì)意識(shí)到使用新的操作符也可以簡(jiǎn)化程序中另一部分的設(shè)計(jì),如此種種。語言和程序一同演進(jìn)。就像交戰(zhàn)兩國的邊界一樣,語言和程序的界限不斷地移動(dòng),直到最終沿著山脈和河流確定下來,這也就是你要解決的問題本身的自然邊界。最后你的程序看起來就好像語言就是為解決它而設(shè)計(jì)的。并且當(dāng)語言和程序彼此都配合得非常完美時(shí),你得到的將是清晰、簡(jiǎn)短和高效的代碼。
需要強(qiáng)調(diào)的是,自底向上的設(shè)計(jì)并不意味著只是換個(gè)次序?qū)懗绦?。?dāng)以自底向上的方式工作時(shí),你通常寫出來的程序會(huì)徹底改觀。你將得到一個(gè)帶有更多抽象操作符的更大的語言,和一個(gè)用它寫的更精練的程序,而不是單個(gè)的整塊的程序。你得到將是拱而非梁。
在典型的程序中,一旦把那些僅僅是做非邏輯工作的部分抽象掉,剩下的代碼就短小多了;你構(gòu)造的語言越高階,程序從上層邏輯到下層語言的距離就越近。這有幾個(gè)好處:
通過讓語言擔(dān)當(dāng)更多的工作,自底向上設(shè)計(jì)產(chǎn)生的程序會(huì)更加短小輕快。一個(gè)更短小的程序就不必劃分成那么多的組件了,并且更少的組件意味著程序會(huì)更易于閱讀和修改。更少的組件也使得著組件之間的連接會(huì)更少,因而錯(cuò)誤發(fā)生的機(jī)會(huì)也會(huì)相應(yīng)減少。一個(gè)機(jī)械設(shè)計(jì)師往往努力去減少機(jī)器上運(yùn)動(dòng)部件的數(shù)量,同樣有經(jīng)驗(yàn)的 Lisp 程序員使用自底向上的設(shè)計(jì)方法來減小他們程序的規(guī)模和復(fù)雜度。
自底向上的設(shè)計(jì)促進(jìn)了代碼重用。當(dāng)你寫兩個(gè)或更多程序時(shí),許多你為第一個(gè)程序?qū)懙墓ぞ咭矔?huì)對(duì)之后的程序開發(fā)有幫助。一旦積累下了雄厚的工具基礎(chǔ),寫一個(gè)新程序所耗費(fèi)的精力和從原始(raw) Lisp 環(huán)境白手起家相比,前者可能只是后者的幾分之一。
譯者注:Montaigne,即MichelRyquemdeMontaigne。國內(nèi)一般譯作"蒙田"。他是法國文藝復(fù)興后期重要的人文主義學(xué)者,他曾說過"我本人就是作品的內(nèi)容"。"但是沒人能讀懂你的程序,除非理解了所有新的實(shí)用函數(shù)"。要想知道為什么這種認(rèn)識(shí)是一種誤解,請(qǐng)參考第4.8 節(jié)。
如果一個(gè)程序中兩個(gè)關(guān)系很遠(yuǎn)的組件在形式上很相似,你就會(huì)因此注意到這種相似性,然后也許會(huì)以更簡(jiǎn)單的方式重新設(shè)計(jì)程序。
對(duì)于其他非 Lisp 的語言來說,自底向上的設(shè)計(jì)在某種程度上也是可能的。大家熟悉的庫函數(shù)就是自底向上設(shè)計(jì)的一種體現(xiàn)。然而在這方面,Lisp 還能提供比其他語言更強(qiáng)大的威力,而且在以 Lisp 風(fēng)格編程時(shí),擴(kuò)展這門語言的重要性也相應(yīng)提高了,所以 Lisp 不僅是一門不同的編程語言,而且是一種完全不一樣的編程方式。
確實(shí),這種開發(fā)風(fēng)格更適合那種可以小規(guī)模開發(fā)的程序。不過,與此同時(shí),它卻讓一個(gè)小組所能做更多的事情。在《人月神話》一書中,F(xiàn)rederickBrooks 提出"一組程序員的生產(chǎn)力并不隨人員的數(shù)量呈線性增長(zhǎng)"。
隨著組內(nèi)人數(shù)的增加,個(gè)體程序員的生產(chǎn)力將有所下降。 Lisp 編程經(jīng)驗(yàn)以一種更加令人振奮的方式重申這個(gè)定律:隨著組內(nèi)人數(shù)的減少,個(gè)體程序員的生產(chǎn)力將會(huì)提高。一個(gè)小組取得成功的原因,僅僅是因?yàn)樗囊?guī)模相對(duì)較小。如果一個(gè)小組能利用 Lisp 帶來的技術(shù)優(yōu)勢(shì),它必定會(huì)走向成功。
隨著軟件復(fù)雜度的提高,編程的 Lisp 風(fēng)格也變得愈加重要。專業(yè)用戶現(xiàn)在對(duì)軟件的要求如此之多以致于我們幾乎無法預(yù)見到他們的所有需求。就算用戶自己也沒辦法預(yù)測(cè)到他們所有的需求。但如果我們不能給他們一個(gè)現(xiàn)成的軟件,讓它能完成用戶想要的每個(gè)功能,那么我們也可以交付一個(gè)可擴(kuò)展的軟件。我們把自己的軟件從單單一個(gè)程序變成了一門編程語言,然后高級(jí)用戶就可以在此基礎(chǔ)上構(gòu)造他們需要的額外特性。
自底向上的設(shè)計(jì)很自然地產(chǎn)生了可擴(kuò)展的程序。最簡(jiǎn)單的自底向上程序包括兩層:語言和程序。復(fù)雜的程序可以被寫成多個(gè)層次,每一層作為其上層的編程語言。如果這一哲學(xué)被一直沿用到最上面的那層,那最上面的這一層對(duì)于用戶來說就變成了一門編程語言。這樣一個(gè)可擴(kuò)展性體現(xiàn)在每一層次的程序,與那些先按照傳統(tǒng)黑盒方法寫成,事后才加上可擴(kuò)展性的那些系統(tǒng)相比,更有可能成為一門好得多的編程語言。
X-Window 和 TEX 就是遵循這一設(shè)計(jì)原則編寫而成的早期典范。在 1980 年代,更強(qiáng)大的硬件使得新一代的 程序能使用 Lisp 作為它們的擴(kuò)展語言。首先是 GNUEmacs,流行的 Unix 文本編輯器。緊接著是 AutoCAD ,第一個(gè)把 Lisp 作為擴(kuò)展語言的大型商業(yè)軟件。1991 年 Interleaf 發(fā)布了他們軟件的新版本,它不僅采用 Lisp 作為擴(kuò)展語言,甚至該軟件大部分就是用 Lisp 實(shí)現(xiàn)的。
Lisp 這門語言特別適合編寫可擴(kuò)展程序,主要原因是因?yàn)樗旧砭褪且粋€(gè)可擴(kuò)展的程序。如果你用 Lisp 寫你的程序以便將這種可擴(kuò)展性轉(zhuǎn)移到用戶那里,你事實(shí)上已經(jīng)毫不費(fèi)力地得到了一個(gè)可擴(kuò)展語言。并且用 Lisp 擴(kuò)展 Lisp 程序,和用一個(gè)傳統(tǒng)語言做同樣的事情相比,它們的區(qū)別就好比面對(duì)面交談和使用書信聯(lián)系的區(qū)別。如果一個(gè)程序只是簡(jiǎn)單提供了一些供外部程序訪問的方式,以期獲得可擴(kuò)展性,那么我們最樂觀的估計(jì)也無非是兩個(gè)黑箱之間彼此通過預(yù)先定義好的渠道進(jìn)行通信。在 Lisp 里,這些擴(kuò)展有權(quán)限直接訪問整個(gè)底層程序。這并不是說你必須授予用戶你程序中每一個(gè)部分的訪問權(quán)限,只是說你現(xiàn)在有機(jī)會(huì)決定是否賦給他們這樣的權(quán)限。
當(dāng)權(quán)限的取舍和交互式環(huán)境結(jié)合在一起,你就擁有了處于最佳狀態(tài)的可擴(kuò)展性。任何軟件,如果你想以它為基礎(chǔ),在其上進(jìn)行擴(kuò)展,為己所用,在你心中就好比有了一張非常大,可能過于巨大的完整的藍(lán)圖。要是其中的有些東西不敢確定,該怎么辦?如果原始程序是用 Lisp 開發(fā)的,那就可以交互式地試探它:你可以檢查它的數(shù)據(jù)結(jié)構(gòu);你可以調(diào)用它的函數(shù);你甚至可能去看它最初的源代碼。這種反饋信息讓你能信心百倍地寫程序 去寫更加雄心勃勃的擴(kuò)展,并且會(huì)寫得更快。一般而言,交互式環(huán)境可以讓編程更輕松,但它對(duì)寫擴(kuò)展的人來說尤其有用。
可擴(kuò)展的程序是一柄雙刃劍,但近來的經(jīng)驗(yàn)表明,和鈍劍相比,用戶更喜歡雙刃劍??蓴U(kuò)展的程序看起來正在流行,無論它們是否暗藏危機(jī)。
1.4 擴(kuò)展 Lisp
有兩種方式可以為 Lisp 增加新的操作符:函數(shù)和宏。在 Lisp 里,你定義的函數(shù)和那些內(nèi)置函數(shù)具有相同的地位。如果想要一個(gè)新的改版的mapcar ,那你就可以先自己定義,然后就像使用mapcar 那樣來使用它。
例如,如果有一個(gè)函數(shù),你想把從 1 到 10 之間的所有整數(shù)分別傳給它,然后把函數(shù)的返回值組成的列表留下,你可以創(chuàng)建一個(gè)新列表然后把它傳給 mapcar :
(mapcar fn
(do* ((x 1 (1+ x))
(result (list x) (push x result)))
((= x 10) (nreverse result))))
但這樣做既不美觀又沒效率。換種辦法,你也可以定義一個(gè)新的映射函數(shù) map1-n (見36 頁),然后像下面那樣調(diào)用它:
(map1-n fn 10)
定義函數(shù)相對(duì)而言比較直截了當(dāng)。而用宏來定義新操作符,雖然更通用,但不太容易理解。宏是用來寫程序的程序。這句話意味深長(zhǎng),深入地探究這個(gè)問題正是本書的主要目的之一。
深思熟慮地使用宏,可以讓程序驚人的清晰簡(jiǎn)潔。這些好處絕非唾手可得。盡管到最后,宏將被視為世上最自然的東西,但最初理解它的時(shí)候卻會(huì)舉步維艱。部分原因是因?yàn)楹瓯群瘮?shù)更加一般化,所以編寫的時(shí)候要考慮的事情更多。但宏難于理解,最主要的原因是它太另類了。沒有任何一門語言有像 Lisp 宏那樣的東西。所以學(xué)習(xí)宏,可能先要從頭腦中清除從其他語言那里潛移默化接受的先入為主的觀念。這些觀念中,首當(dāng)其沖就是為那些陳詞濫調(diào)所累的程序。憑什么數(shù)據(jù)結(jié)構(gòu)可以變化,并且其中的數(shù)據(jù)可以修改,而程序卻不能呢?在 Lisp 里,程序就是數(shù)據(jù),但其中深意需要假以時(shí)日才能體會(huì)到。
如果你需要花些時(shí)間才能習(xí)慣宏,那么這些時(shí)間絕對(duì)是值得的。即使像迭代這樣平淡無奇的用法中,宏也可以使程序明顯變得更短小精悍。假設(shè)一個(gè)程序需要在某個(gè)程序體上從 a 到 b 來迭代x 。Lisp 內(nèi)置的 do 可以用于更加一般的場(chǎng)合。而對(duì)于簡(jiǎn)單的迭代來說,用它并不能寫出可讀性最好的代碼:
(do ((x a (+ 1 x))) ((> x b)) (print x))
另一方面,假如我們可以只寫成這樣:
(for (x a b) (print x))
宏使這成為可能。用六行代碼(見第104 頁),我們就能把 for 加入到語言中,就好像原裝的一樣。并且正如后面的章節(jié)所展示的,寫個(gè) for 對(duì)宏的廣闊天地來說,不過是小試牛刀。
沒有人對(duì)你橫加限制,說每次只能為 Lisp 擴(kuò)展一個(gè)函數(shù)或是宏。如果需要,你可以在 Lisp 之上構(gòu)造一個(gè)完整的語言,然后用它來編寫程序。 Lisp 對(duì)于寫編譯器和解釋器來說是極為優(yōu)秀的語言,但它定義新語言的方式和以往完全不同,這種方式通常更加簡(jiǎn)潔,而且自然,也更省力:即在原有的 Lisp 基礎(chǔ)上加以修改,成為一門新的語言。這樣,Lisp 中保持不變部分可以在新語言里(例如數(shù)學(xué)計(jì)算或者I/O 操作) 得以繼續(xù)沿用,
你只需要實(shí)現(xiàn)有變化的那部分(例如控制結(jié)構(gòu))。以這種方式實(shí)現(xiàn)的語言被稱為嵌入式語言。
嵌入式語言是自底向上程序設(shè)計(jì)的自然產(chǎn)物。Common Lisp 里已經(jīng)有了好幾種這樣的語言。其中最著名的 ??? 將在最后一章里討論。但你也可以定義自己的嵌入式語言。然后就能得到一個(gè)完全為你程序度身定制的語言,甚至它們最后看起來跟 Lisp 已經(jīng)截然不同。
這些新的可能性并非來自某一個(gè)神奇的源頭。這樣說吧,Lisp 就像一個(gè)拱頂。究竟哪一塊楔形石頭(拱石)托起了整個(gè)拱呢?這個(gè)問題本身就是錯(cuò)誤的;每一塊都是。和拱一樣,Lisp 是一組相互契合的特性的集合。
你也可以使用 Common Lisp 的series 宏把代碼寫得更簡(jiǎn)潔,但那也只能證明同樣的觀點(diǎn),因?yàn)檫@些宏就是 Lisp 本身的擴(kuò)展。
我們可以列出這些特性中的一部分:動(dòng)態(tài)存儲(chǔ)分配和垃圾收集、運(yùn)行時(shí)類型系統(tǒng)、函數(shù)對(duì)象、生成列表的內(nèi)置解析器、一個(gè)接受列表形式的程序的編譯器、交互式環(huán)境等等,但 Lisp 的威力不能單單歸功于它們中的任何一個(gè)。是上述這些特性一同造就了 Lisp 編程現(xiàn)在的模樣。
在過去的二十年間,人們的編程方式發(fā)生了變化。其中許多變化 交互式環(huán)境、動(dòng)態(tài)鏈接,甚至面向?qū)ο蟮某绦蛟O(shè)計(jì),就是一次又一次的嘗試,它們把 Lisp 的一些靈活性帶給其它編程語言。關(guān)于拱頂?shù)哪莻€(gè)比喻說明了這些嘗試是怎樣的成功。
眾所周知,Lisp 和 Fortran 是目前仍在使用中的兩門最古老的編程語言。可能更有意思的是,它們?cè)谡Z言設(shè)計(jì)的哲學(xué)上代表了截然相反的兩個(gè)極端。Fortran 被發(fā)明出來以替代匯編語言。Lisp 被發(fā)明出來表述算法。如此截然不同的意圖產(chǎn)生了迥異的兩門語言,F(xiàn)ortran 使編譯器作者的生活更輕松;而 Lisp 則讓程序員的生活更舒服。自從那時(shí)起,大多數(shù)編程語言都落在了兩極之間。Fortran 和 Lisp 它們自己也逐漸在向中間地帶靠攏。Fortran 現(xiàn)在看起來更像Algol 了,而 Lisp 也改掉了它年幼時(shí)一些很低效的語言習(xí)慣。
最初的 Fortran 和 Lisp 在某種程度上定義了一個(gè)戰(zhàn)場(chǎng)。戰(zhàn)場(chǎng)的一邊的口號(hào)是"效率!(并且,還有幾乎不可能實(shí)現(xiàn)。)" 在戰(zhàn)場(chǎng)的另一邊,口號(hào)是"抽象!(并且不管怎么說,這不是產(chǎn)品級(jí)軟件。)" 就好像諸神在冥冥之中決定古希臘戰(zhàn)爭(zhēng)的勝敗那樣,編程語言這場(chǎng)戰(zhàn)爭(zhēng)的結(jié)局取決于硬件。每一年都在往 Lisp 更有利的方向發(fā)展?,F(xiàn)在對(duì) Lisp 的爭(zhēng)議聽起來已經(jīng)有點(diǎn)兒像1970 年代早期匯編語言程序員對(duì)于高級(jí)語言的論點(diǎn)。
問題不再是為什么用 Lisp?而是何時(shí)用 Lisp?
更多建議: