文章的最后部分代碼沒有調(diào)整格式像樊,建議去原文閱讀:http://www.nowamagic.net/librarys/veda/detail/201
http://www.ruanyifeng.com/blog/2010/10/why_lisp_is_superior.html
一
如果我們把流行的編程語(yǔ)言浊闪,以這樣的順序排列:Java扬霜、Perl、Python救湖、Ruby向图。你會(huì)發(fā)現(xiàn),排在越后面的語(yǔ)言丐枉,越像Lisp。
Python模仿Lisp掘托,甚至把許多Lisp黑客認(rèn)為屬于設(shè)計(jì)錯(cuò)誤的功能瘦锹,也一起模仿了。至于Ruby闪盔,如果回到1975年弯院,你聲稱它是一種Lisp方言,沒有人會(huì)反對(duì)泪掀。
編程語(yǔ)言現(xiàn)在的發(fā)展听绳,不過剛剛趕上1958年Lisp語(yǔ)言的水平。
二
1958年异赫,John McCarthy設(shè)計(jì)了Lisp語(yǔ)言辫红。我認(rèn)為,當(dāng)前最新潮的編程語(yǔ)言祝辣,只是實(shí)現(xiàn)了他在1958年的設(shè)想而已。
這怎么可能呢切油?計(jì)算機(jī)技術(shù)的發(fā)展蝙斜,不是日新月異嗎?1958年的技術(shù)澎胡,怎么可能超過今天的水平呢孕荠?
讓我告訴你原因。
這是因?yàn)镴ohn McCarthy本來(lái)沒打算把Lisp設(shè)計(jì)成編程語(yǔ)言攻谁,至少不是我們現(xiàn)在意義上的編程語(yǔ)言稚伍。他的原意只是想做一種理論演算,用更簡(jiǎn)潔的方式定義圖靈機(jī)戚宦。
所以个曙,為什么上個(gè)世紀(jì)50年代的編程語(yǔ)言,到現(xiàn)在還沒有過時(shí)受楼?簡(jiǎn)單說垦搬,因?yàn)檫@種語(yǔ)言本質(zhì)上不是一種技術(shù)呼寸,而是數(shù)學(xué)。數(shù)學(xué)是不會(huì)過時(shí)的猴贰。你不應(yīng)該把Lisp語(yǔ)言與50年代的硬件聯(lián)系在一起对雪,而是應(yīng)該把它與快速排序(Quicksort)算法進(jìn)行類比。這種算法是1960年提出的米绕,至今仍然是最快的通用排序方法瑟捣。
三
Fortran語(yǔ)言也是上個(gè)世紀(jì)50年代出現(xiàn)的,并且一直使用至今栅干。它代表了語(yǔ)言設(shè)計(jì)的一種完全不同的方向迈套。Lisp是無(wú)意中從純理論發(fā)展為編程語(yǔ)言,而Fortran從一開始就是作為編程語(yǔ)言設(shè)計(jì)出來(lái)的非驮。但是交汤,今天我們把Lisp看成高級(jí)語(yǔ)言,而把Fortran看成一種相當(dāng)?shù)蛯哟蔚恼Z(yǔ)言劫笙。
1956年芙扎,F(xiàn)ortran剛誕生的時(shí)候,叫做Fortran I填大,與今天的Fortran語(yǔ)言差別極大戒洼。Fortran I實(shí)際上是匯編語(yǔ)言加上數(shù)學(xué),在某些方面允华,還不如今天的匯編語(yǔ)言強(qiáng)大圈浇。比如,它不支持子程序靴寂,只有分支跳轉(zhuǎn)結(jié)構(gòu)(branch)磷蜀。
Lisp和Fortran代表了編程語(yǔ)言發(fā)展的兩大方向。前者的基礎(chǔ)是數(shù)學(xué)百炬,后者的基礎(chǔ)是硬件架構(gòu)褐隆。從那時(shí)起,這兩大方向一直在互相靠攏剖踊。Lisp剛設(shè)計(jì)出來(lái)的時(shí)候庶弃,就很強(qiáng)大,接下來(lái)的二十年德澈,它提高了自己的運(yùn)行速度歇攻。而那些所謂的主流語(yǔ)言,把更快的運(yùn)行速度作為設(shè)計(jì)的出發(fā)點(diǎn)梆造,然后再用超過四十年的時(shí)間缴守,一步步變得更強(qiáng)大。
直到今天,最高級(jí)的主流語(yǔ)言斧散,也只是剛剛接近Lisp的水平供常。雖然已經(jīng)很接近了,但還是沒有Lisp那樣強(qiáng)大鸡捐。
四
Lisp語(yǔ)言誕生的時(shí)候栈暇,就包含了9種新思想。其中一些我們今天已經(jīng)習(xí)以為常箍镜,另一些則剛剛在其他高級(jí)語(yǔ)言中出現(xiàn)源祈,至今還有2種是Lisp獨(dú)有的。按照被大眾接受的程度色迂,這9種思想依次是:
條件結(jié)構(gòu)(即"if-then-else"結(jié)構(gòu))∠闳保現(xiàn)在大家都覺得這是理所當(dāng)然的,但是Fortran I就沒有這個(gè)結(jié)構(gòu)歇僧,它只有基于底層機(jī)器指令的goto結(jié)構(gòu)图张。
函數(shù)也是一種數(shù)據(jù)類型。在Lisp語(yǔ)言中诈悍,函數(shù)與整數(shù)或字符串一樣祸轮,也屬于數(shù)據(jù)類型的一種。它有自己的字面表示形式(literal representation)侥钳,能夠儲(chǔ)存在變量中适袜,也能當(dāng)作參數(shù)傳遞。一種數(shù)據(jù)類型應(yīng)該有的功能舷夺,它都有苦酱。
遞歸。Lisp是第一種支持遞歸函數(shù)的高級(jí)語(yǔ)言给猾。
變量的動(dòng)態(tài)類型疫萤。在Lisp語(yǔ)言中,所有變量實(shí)際上都是指針敢伸,所指向的值有類型之分给僵,而變量本身沒有。復(fù)制變量就相當(dāng)于復(fù)制指針详拙,而不是復(fù)制它們指向的數(shù)據(jù)。
垃圾回收機(jī)制蔓同。
程序由表達(dá)式(expression)組成饶辙。Lisp程序是一些表達(dá)式區(qū)塊的集合,每個(gè)表達(dá)式都返回一個(gè)值斑粱。這與Fortran和大多數(shù)后來(lái)的語(yǔ)言都截然不同弃揽,它們的程序由表達(dá)式和語(yǔ)句(statement)組成。
區(qū)分表達(dá)式和語(yǔ)句,在Fortran I中是很自然的矿微,因?yàn)樗恢С终Z(yǔ)句嵌套痕慢。所以,如果你需要用數(shù)學(xué)式子計(jì)算一個(gè)值涌矢,那就只有用表達(dá)式返回這個(gè)值掖举,沒有其他語(yǔ)法結(jié)構(gòu)可用,因?yàn)榉駝t就無(wú)法處理這個(gè)值娜庇。
后來(lái)塔次,新的編程語(yǔ)言支持區(qū)塊結(jié)構(gòu)(block),這種限制當(dāng)然也就不存在了名秀。但是為時(shí)已晚励负,表達(dá)式和語(yǔ)句的區(qū)分已經(jīng)根深蒂固。它從Fortran擴(kuò)散到Algol語(yǔ)言匕得,接著又?jǐn)U散到它們兩者的后繼語(yǔ)言继榆。
符號(hào)(symbol)類型。符號(hào)實(shí)際上是一種指針汁掠,指向儲(chǔ)存在哈希表中的字符串略吨。所以,比較兩個(gè)符號(hào)是否相等调塌,只要看它們的指針是否一樣就行了晋南,不用逐個(gè)字符地比較。
代碼使用符號(hào)和常量組成的樹形表示法(notation)羔砾。
無(wú)論什么時(shí)候负间,整個(gè)語(yǔ)言都是可用的。Lisp并不真正區(qū)分讀取期姜凄、編譯期和運(yùn)行期政溃。你可以在讀取期編譯或運(yùn)行代碼;也可以在編譯期讀取或運(yùn)行代碼态秧;還可以在運(yùn)行期讀取或者編譯代碼董虱。
在讀取期運(yùn)行代碼,使得用戶可以重新調(diào)整(reprogram)Lisp的語(yǔ)法申鱼;在編譯期運(yùn)行代碼愤诱,則是Lisp宏的工作基礎(chǔ);在運(yùn)行期編譯代碼捐友,使得Lisp可以在Emacs這樣的程序中淫半,充當(dāng)擴(kuò)展語(yǔ)言(extension language);在運(yùn)行期讀取代碼匣砖,使得程序之間可以用S-表達(dá)式(S-expression)通信科吭,近來(lái)XML格式的出現(xiàn)使得這個(gè)概念被重新"發(fā)明"出來(lái)了昏滴。
五
Lisp語(yǔ)言剛出現(xiàn)的時(shí)候,它的思想與其他編程語(yǔ)言大相徑庭对人。后者的設(shè)計(jì)思想主要由50年代后期的硬件決定谣殊。隨著時(shí)間流逝,流行的編程語(yǔ)言不斷更新?lián)Q代牺弄,語(yǔ)言設(shè)計(jì)思想逐漸向Lisp靠攏姻几。
思想1到思想5已經(jīng)被廣泛接受,思想6開始在主流編程語(yǔ)言中出現(xiàn)猖闪,思想7在Python語(yǔ)言中有所實(shí)現(xiàn)鲜棠,不過似乎沒有專用的語(yǔ)法。
思想8可能是最有意思的一點(diǎn)培慌。它與思想9只是由于偶然原因豁陆,才成為L(zhǎng)isp語(yǔ)言的一部分,因?yàn)樗鼈儾粚儆贘ohn McCarthy的原始構(gòu)想吵护,是由他的學(xué)生Steve Russell自行添加的盒音。它們從此使得Lisp看上去很古怪,但也成為了這種語(yǔ)言最獨(dú)一無(wú)二的特點(diǎn)馅而。Lisp古怪的形式祥诽,倒不是因?yàn)樗恼Z(yǔ)法很古怪,而是因?yàn)樗緵]有語(yǔ)法瓮恭,程序直接以解析樹(parse tree)的形式表達(dá)出來(lái)雄坪。在其他語(yǔ)言中,這種形式只是經(jīng)過解析在后臺(tái)產(chǎn)生屯蹦,但是Lisp直接采用它作為表達(dá)形式维哈。它由列表構(gòu)成,而列表則是Lisp的基本數(shù)據(jù)結(jié)構(gòu)登澜。
用一門語(yǔ)言自己的數(shù)據(jù)結(jié)構(gòu)來(lái)表達(dá)該語(yǔ)言阔挠,這被證明是非常強(qiáng)大的功能。思想8和思想9脑蠕,意味著你可以寫出一種能夠自己編程的程序购撼。這可能聽起來(lái)很怪異,但是對(duì)于Lisp語(yǔ)言卻是再普通不過谴仙。最常用的做法就是使用宏迂求。
術(shù)語(yǔ)"宏"在Lisp語(yǔ)言中,與其他語(yǔ)言中的意思不一樣晃跺。Lisp宏無(wú)所不包揩局,它既可能是某樣表達(dá)式的縮略形式,也可能是一種新語(yǔ)言的編譯器哼审。如果你想真正地理解Lisp語(yǔ)言谐腰,或者想拓寬你的編程視野,那么你必須學(xué)習(xí)宏涩盾。
就我所知十气,宏(采用Lisp語(yǔ)言的定義)目前仍然是Lisp獨(dú)有的。一個(gè)原因是為了使用宏春霍,你大概不得不讓你的語(yǔ)言看上去像Lisp一樣古怪砸西。另一個(gè)可能的原因是,如果你想為自己的語(yǔ)言添上這種終極武器址儒,你從此就不能聲稱自己發(fā)明了新語(yǔ)言芹枷,只能說發(fā)明了一種Lisp的新方言。
我把這件事當(dāng)作笑話說出來(lái)莲趣,但是事實(shí)就是如此鸳慈。如果你創(chuàng)造了一種新語(yǔ)言,其中有car喧伞、cdr走芋、cons、quote潘鲫、cond翁逞、atom、eq這樣的功能溉仑,還有一種把函數(shù)寫成列表的表示方法挖函,那么在它們的基礎(chǔ)上,你完全可以推導(dǎo)出Lisp語(yǔ)言的所有其他部分浊竟。事實(shí)上怨喘,Lisp語(yǔ)言就是這樣定義的,John McCarthy把語(yǔ)言設(shè)計(jì)成這個(gè)樣子逐沙,就是為了讓這種推導(dǎo)成為可能哲思。
六
就算Lisp確實(shí)代表了目前主流編程語(yǔ)言不斷靠近的一個(gè)方向,這是否意味著你就應(yīng)該用它編程呢吩案?
如果使用一種不那么強(qiáng)大的語(yǔ)言棚赔,你又會(huì)有多少損失呢?有時(shí)不采用最尖端的技術(shù)徘郭,不也是一種明智的選擇嗎靠益?這么多人使用主流編程語(yǔ)言,這本身不也說明那些語(yǔ)言有可取之處嗎残揉?
另一方面胧后,選擇哪一種編程語(yǔ)言,許多項(xiàng)目是無(wú)所謂的抱环,反正不同的語(yǔ)言都能完成工作壳快。一般來(lái)說纸巷,條件越苛刻的項(xiàng)目,強(qiáng)大的編程語(yǔ)言就越能發(fā)揮作用眶痰。但是瘤旨,無(wú)數(shù)的項(xiàng)目根本沒有苛刻條件的限制。大多數(shù)的編程任務(wù)竖伯,可能只要寫一些很小的程序存哲,然后用膠水語(yǔ)言把這些小程序連起來(lái)就行了。你可以用自己熟悉的編程語(yǔ)言七婴,或者用對(duì)于特定項(xiàng)目來(lái)說有著最強(qiáng)大函數(shù)庫(kù)的語(yǔ)言祟偷,來(lái)寫這些小程序。如果你只是需要在Windows應(yīng)用程序之間傳遞數(shù)據(jù)打厘,使用Visual Basic照樣能達(dá)到目的修肠。
那么,Lisp的編程優(yōu)勢(shì)體現(xiàn)在哪里呢婚惫?
七
語(yǔ)言的編程能力越強(qiáng)大氛赐,寫出來(lái)的程序就越短(當(dāng)然不是指字符數(shù)量,而是指獨(dú)立的語(yǔ)法單位)先舷。
代碼的數(shù)量很重要艰管,因?yàn)殚_發(fā)一個(gè)程序耗費(fèi)的時(shí)間,主要取決于程序的長(zhǎng)度蒋川。如果同一個(gè)軟件牲芋,一種語(yǔ)言寫出來(lái)的代碼比另一種語(yǔ)言長(zhǎng)三倍,這意味著你開發(fā)它耗費(fèi)的時(shí)間也會(huì)多三倍捺球。而且即使你多雇傭人手缸浦,也無(wú)助于減少開發(fā)時(shí)間,因?yàn)楫?dāng)團(tuán)隊(duì)規(guī)模超過某個(gè)門檻時(shí)氮兵,再增加人手只會(huì)帶來(lái)凈損失裂逐。Fred Brooks在他的名著《人月神話》(The Mythical Man-Month)中,描述了這種現(xiàn)象泣栈,我的所見所聞?dòng)∽C了他的說法卜高。
如果使用Lisp語(yǔ)言,能讓程序變得多短?以Lisp和C的比較為例,我聽到的大多數(shù)說法是C代碼的長(zhǎng)度是Lisp的7倍到10倍大猛。但是最近,New Architect雜志上有一篇介紹ITA軟件公司的文章薪缆,里面說"一行Lisp代碼相當(dāng)于20行C代碼",因?yàn)榇宋亩际且肐TA總裁的話伞广,所以我想這個(gè)數(shù)字來(lái)自ITA的編程實(shí)踐拣帽。 如果真是這樣疼电,那么我們可以相信這句話。ITA的軟件减拭,不僅使用Lisp語(yǔ)言澜沟,還同時(shí)大量使用C和C++,所以這是他們的經(jīng)驗(yàn)談峡谊。
根據(jù)上面的這個(gè)數(shù)字,如果你與ITA競(jìng)爭(zhēng)刊苍,而且你使用C語(yǔ)言開發(fā)軟件既们,那么ITA的開發(fā)速度將比你快20倍。如果你需要一年時(shí)間實(shí)現(xiàn)某個(gè)功能正什,它只需要不到三星期啥纸。反過來(lái)說,如果某個(gè)新功能婴氮,它開發(fā)了三個(gè)月斯棒,那么你需要五年才能做出來(lái)。
你知道嗎主经?上面的對(duì)比荣暮,還只是考慮到最好的情況。當(dāng)我們只比較代碼數(shù)量的時(shí)候罩驻,言下之意就是假設(shè)使用功能較弱的語(yǔ)言穗酥,也能開發(fā)出同樣的軟件。但是事實(shí)上惠遏,程序員使用某種語(yǔ)言能做到的事情砾跃,是有極限的。如果你想用一種低層次的語(yǔ)言节吮,解決一個(gè)很難的問題抽高,那么你將會(huì)面臨各種情況極其復(fù)雜、乃至想不清楚的窘境透绩。
所以翘骂,當(dāng)我說假定你與ITA競(jìng)爭(zhēng),你用五年時(shí)間做出的東西渺贤,ITA在Lisp語(yǔ)言的幫助下只用三個(gè)月就完成了雏胃,我指的五年還是一切順利、沒有犯錯(cuò)誤志鞍、也沒有遇到太大麻煩的五年瞭亮。事實(shí)上,按照大多數(shù)公司的實(shí)際情況固棚,計(jì)劃中五年完成的項(xiàng)目统翩,很可能永遠(yuǎn)都不會(huì)完成仙蚜。
我承認(rèn),上面的例子太極端厂汗。ITA似乎有一批非常聰明的黑客委粉,而C語(yǔ)言又是一種很低層次的語(yǔ)言。但是娶桦,在一個(gè)高度競(jìng)爭(zhēng)的市場(chǎng)中贾节,即使開發(fā)速度只相差兩三倍,也足以使得你永遠(yuǎn)處在落后的位置衷畦。
附錄:編程能力
為了解釋我所說的語(yǔ)言編程能力不一樣栗涂,請(qǐng)考慮下面的問題。我們需要寫一個(gè)函數(shù)祈争,它能夠生成累加器斤程,即這個(gè)函數(shù)接受一個(gè)參數(shù)n,然后返回另一個(gè)函數(shù)菩混,后者接受參數(shù)i忿墅,然后返回n增加(increment)了i后的值。
Common Lisp的寫法如下:
1
(defun foo (n)
2
(lambda (i) (incf n i)))
Ruby的寫法幾乎完全相同:
1
def foo (n)
2
lambda {|i| n += i } end
Perl 5的寫法則是:
1
sub foo {
2
my ($n) = @_;
3
sub {$n += shift}
4
}
這比Lisp和Ruby的版本沮峡,有更多的語(yǔ)法元素疚脐,因?yàn)樵赑erl語(yǔ)言中,你不得不手工提取參數(shù)邢疙。
Smalltalk的寫法稍微比Lisp和Ruby的長(zhǎng)一點(diǎn):
1
foo: n
2
|s|
3
s := n.
4
^[:i| s := s+i. ]
因?yàn)樵赟malltalk中亮曹,局部變量(lexical variable)是有效的,但是你無(wú)法給一個(gè)參數(shù)賦值秘症,因此不得不設(shè)置了一個(gè)新變量照卦,接受累加后的值。
Javascript的寫法也比Lisp和Ruby稍微長(zhǎng)一點(diǎn)乡摹,因?yàn)镴avascript依然區(qū)分語(yǔ)句和表達(dá)式役耕,所以你需要明確指定return語(yǔ)句,來(lái)返回一個(gè)值:
1
function foo (n) {
2
return function (i) {
3
return n += i } }
實(shí)事求是地說聪廉,Perl也保留了語(yǔ)句和表達(dá)式的區(qū)別瞬痘,但是使用了典型的Perl方式處理,使你可以省略return板熊。
如果想把Lisp/Ruby/Perl/Smalltalk/Javascript的版本改成Python框全,你會(huì)遇到一些限制。因?yàn)镻ython并不完全支持局部變量干签,你不得不創(chuàng)造一種數(shù)據(jù)結(jié)構(gòu)津辩,來(lái)接受n的值。而且盡管Python確實(shí)支持函數(shù)數(shù)據(jù)類型,但是沒有一種字面量的表示方式(literal representation)可以生成函數(shù)(除非函數(shù)體只有一個(gè)表達(dá)式)喘沿,所以你需要?jiǎng)?chuàng)造一個(gè)命名函數(shù)闸度,把它返回。最后的寫法如下:
1
def foo (n):
2
s = [n]
3
def bar (i):
4
s[0] += i
5
return s[0]
6
return bar
Python用戶完全可以合理地質(zhì)疑蚜印,為什么不能寫成下面這樣:
1
def foo (n):
2
return lambda i: return n += i
或者:
1
def foo (n):
2
lambda i: n += i
我猜想莺禁,Python有一天會(huì)支持這樣的寫法。(如果你不想等到Python慢慢進(jìn)化到更像Lisp窄赋,你總是可以直接......)
在面向?qū)ο缶幊痰恼Z(yǔ)言中哟冬,你能夠在有限程度上模擬一個(gè)閉包(即一個(gè)函數(shù),通過它可以引用由包含這個(gè)函數(shù)的代碼所定義的變量)忆绰。你定義一個(gè)類(class)柒傻,里面有一個(gè)方法和一個(gè)屬性,用于替換封閉作用域(enclosing scope)中的所有變量较木。這有點(diǎn)類似于讓程序員自己做代碼分析,本來(lái)這應(yīng)該是由支持局部作用域的編譯器完成的青柄。如果有多個(gè)函數(shù)伐债,同時(shí)指向相同的變量,那么這種方法就會(huì)失效致开,但是在這個(gè)簡(jiǎn)單的例子中峰锁,它已經(jīng)足夠了。
Python高手看來(lái)也同意双戳,這是解決這個(gè)問題的比較好的方法虹蒋,寫法如下:
1
def foo (n):
2
class acc:
3
def _ init _ (self, s):
4
self.s = s
5
def inc (self, i):
6
self.s += i
7
return self.s
8
return acc (n).inc
或者
1
class foo:
2
def _ init _ (self, n):
3
self.n = n
4
def _ call _ (self, i):
5
self.n += i
6
return self.n
我添加這一段,原因是想避免Python愛好者說我誤解這種語(yǔ)言飒货。但是魄衅,在我看來(lái),這兩種寫法好像都比第一個(gè)版本更復(fù)雜塘辅。你實(shí)際上就是在做同樣的事晃虫,只不過劃出了一個(gè)獨(dú)立的區(qū)域,保存累加器函數(shù)扣墩,區(qū)別只是保存在對(duì)象的一個(gè)屬性中哲银,而不是保存在列表(list)的頭(head)中。使用這些特殊的內(nèi)部屬性名(尤其是call)呻惕,看上去并不像常規(guī)的解法荆责,更像是一種破解。
1
在Perl和Python的較量中亚脆,Python黑客的觀點(diǎn)似乎是認(rèn)為Python比Perl更優(yōu)雅做院,但是這個(gè)例子表明,最終來(lái)說,編程能力決定了優(yōu)雅山憨。Perl的寫法更簡(jiǎn)單(包含更少的語(yǔ)法元素)查乒,盡管它的語(yǔ)法有一點(diǎn)丑陋。
其他語(yǔ)言怎么樣郁竟?前文曾經(jīng)提到過Fortran玛迄、C、C++棚亩、Java和Visual Basic蓖议,看上去使用它們,根本無(wú)法解決這個(gè)問題讥蟆。Ken Anderson說勒虾,Java只能寫出一個(gè)近似的解法:
01
public interface Inttoint {
02
public int call (int i);
03
}
04
public static Inttoint foo (final int n) {
05
return new Inttoint () {
06
int s = n;
07
public int call (int i) {
08
s = s + i;
09
return s;
10
}};
11
}
這種寫法不符合題目要求,因?yàn)樗粚?duì)整數(shù)有效瘸彤。
當(dāng)然修然,我說使用其他語(yǔ)言無(wú)法解決這個(gè)問題,這句話并不完全正確质况。所有這些語(yǔ)言都是圖靈等價(jià)的愕宋,這意味著嚴(yán)格地說,你能使用它們之中的任何一種語(yǔ)言结榄,寫出任何一個(gè)程序中贝。那么,怎樣才能做到這一點(diǎn)呢臼朗?就這個(gè)小小的例子而言邻寿,你可以使用這些不那么強(qiáng)大的語(yǔ)言,寫一個(gè)Lisp解釋器就行了视哑。
這樣做聽上去好像開玩笑绣否,但是在大型編程項(xiàng)目中,卻不同程度地廣泛存在挡毅。因此枝秤,有人把它總結(jié)出來(lái),起名為"格林斯潘第十定律"(Greenspun's Tenth Rule):
"任何C或Fortran程序復(fù)雜到一定程度之后慷嗜,都會(huì)包含一個(gè)臨時(shí)開發(fā)的淀弹、只有一半功能的、不完全符合規(guī)格的庆械、到處都是bug的薇溃、運(yùn)行速度很慢的Common Lisp實(shí)現(xiàn)。"
如果你想解決一個(gè)困難的問題缭乘,關(guān)鍵不是你使用的語(yǔ)言是否強(qiáng)大沐序,而是好幾個(gè)因素同時(shí)發(fā)揮作用:
使用一種強(qiáng)大的語(yǔ)言。
為這個(gè)難題寫一個(gè)事實(shí)上的解釋器。
你自己變成這個(gè)難題的人肉編譯器策幼。
在Python的例子中邑时,這樣的處理方法已經(jīng)開始出現(xiàn)了,我們實(shí)際上就是自己寫代碼特姐,模擬出編譯器實(shí)現(xiàn)局部變量的功能晶丘。
這種實(shí)踐不僅很普遍,而且已經(jīng)制度化了唐含。舉例來(lái)說浅浮,在面向?qū)ο缶幊痰氖澜缰校覀兇罅柯牭?模式"(pattern)這個(gè)詞捷枯,我覺得那些"模式"就是現(xiàn)實(shí)中的因素(c)滚秩,也就是人肉編譯器。 當(dāng)我在自己的程序中淮捆,發(fā)現(xiàn)用到了模式郁油,我覺得這就表明某個(gè)地方出錯(cuò)了。程序的形式攀痊,應(yīng)該僅僅反映它所要解決的問題桐腌。代碼中其他任何外加的形式,都是一個(gè)信號(hào)蚕苇,(至少對(duì)我來(lái)說)表明我對(duì)問題的抽象還不夠深,也經(jīng)常提醒我凿叠,自己正在手工完成的事情涩笤,本應(yīng)該寫代碼,通過宏的擴(kuò)展自動(dòng)實(shí)現(xiàn)