日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Lisp已死,Lisp萬(wàn)歲!

有一句古話,叫做“國(guó)王已死,國(guó)王萬(wàn)歲!”它的意思是,老國(guó)王已經(jīng)死去,國(guó)王的兒子現(xiàn)在繼位。這句話的幽默,就在于這兩個(gè)“國(guó)王”其實(shí)指的不是同一個(gè)人,而你咋一看還以為它自相矛盾。今天我的話題仿效了這句話,叫做“Lisp 已死,Lisp 萬(wàn)歲!”希望到***你會(huì)明白這是什么意思。

潮安網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,潮安網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為潮安1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站制作要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的潮安做網(wǎng)站的公司定做!

首先,我想總結(jié)一下 Lisp 的優(yōu)點(diǎn)。你也許已經(jīng)知道,Lisp 身上最重要的一些優(yōu)點(diǎn),其實(shí)已經(jīng)“遺傳”到了幾乎每種流行的語(yǔ)言身上(Java,C#,JavaScript,Python, Ruby,Haskell,……)。由于我已經(jīng)在其他博文里詳細(xì)的敘述過(guò)其中一些,所以現(xiàn)在只把這些 Lisp 的優(yōu)點(diǎn)簡(jiǎn)單列出來(lái)(關(guān)鍵部分加了鏈接):

  • Lisp 的語(yǔ)法是世界上最精煉,最美觀,也是語(yǔ)法分析起來(lái)***效的語(yǔ)法。這是 Lisp ***的,其他語(yǔ)言都沒(méi)有的優(yōu)點(diǎn)。有些人喜歡設(shè)計(jì)看起來(lái)很炫的語(yǔ)法,其實(shí)都是自找麻煩。為什么這么說(shuō)呢,請(qǐng)參考這篇《談?wù)Z法》。

  • Lisp 是***個(gè)可以在程序的任何位置定義函數(shù),并且可以把函數(shù)作為值傳遞的語(yǔ)言。這樣的設(shè)計(jì)使得它的表達(dá)能力非常強(qiáng)大。這種理念被 Python,JavaScript,Ruby 等語(yǔ)言所借鑒。

  • Lisp 有世界上***大的宏系統(tǒng)(macro system)。這種宏系統(tǒng)的表達(dá)力幾乎達(dá)到了理論所允許的極限。如果你只見(jiàn)過(guò) C 語(yǔ)言的“宏”,那我可以告訴你它是完全沒(méi)法跟 Lisp 的宏系統(tǒng)相提并論的。

  • Lisp 是世界上***個(gè)使用垃圾回收(garbage collection)的語(yǔ)言。這種超前的理念,后來(lái)被 Java,C# 等語(yǔ)言借鑒。

想不到吧,現(xiàn)代語(yǔ)言的很多優(yōu)點(diǎn),其實(shí)都是來(lái)自于 Lisp — 世界上第二古老的程序語(yǔ)言。所以有人才會(huì)說(shuō),每一種現(xiàn)代語(yǔ)言都在朝著 Lisp 的方向“進(jìn)化”。如果你相信了這話,也許就會(huì)疑惑,為什么 Lisp 今天沒(méi)有成為主流,為什么 Lisp Machine 會(huì)被 Unix 打敗。其實(shí)除了商業(yè)原因之外,還有技術(shù)上的問(wèn)題

早期的 Lisp 其實(shí)普遍存在一個(gè)非常嚴(yán)重的問(wèn)題:它使用 dynamic scoping。所謂 dynamic scoping 就是說(shuō),如果你的函數(shù)定義里面有“自由變量”,那么這個(gè)自由變量的值,會(huì)隨著函數(shù)的“調(diào)用位置”的不同而發(fā)生變化。

比如下面我定義一個(gè)函數(shù) f,它接受一個(gè)參數(shù) y,然后返回 x 和 y 的積。

 
 
 
  1. (setq f 
  2.       (let ((x 1)) 
  3.         (lambda (y) (* x y))))

這里 x 對(duì)于函數(shù) (lambda (y) (* x y)) 來(lái)說(shuō)是個(gè)“自由變量”(free variable),因?yàn)樗皇撬膮?shù)

看著這段代碼,你會(huì)很自然的認(rèn)為,因?yàn)?x 的值是 1,那么 f 被調(diào)用的時(shí)候,結(jié)果應(yīng)該等于 (* 1 y),也就是說(shuō)應(yīng)該等于 y 的值??墒沁@在 dynamic scoping 的語(yǔ)言里結(jié)果如何呢?我們來(lái)看看吧。

(你可以在 emacs 里面試驗(yàn)以下的結(jié)果,因?yàn)?Emacs Lisp 使用的就是 dynamic scoping。)

如果我們?cè)诤瘮?shù)調(diào)用的外層定義一個(gè) x,值為 2:

  
 
 
  1. (let ((x 2))
  2.   (funcall f 2))

因?yàn)檫@個(gè) x 跟 f 定義處的 x 的作用域不同,所以它們不應(yīng)該互相干擾。所以我們應(yīng)該得到 2。可是,這段代碼返回的結(jié)果卻為 4。

再來(lái)。我們另外定義一個(gè) x,值為 3:

  
 
 
  1. (let ((x 3))
  2.   (funcall f 2))

我們的期望值還是 2,可是結(jié)果卻是 6。

再來(lái)。如果我們直接調(diào)用:

 
 
 
  1. (funcall f 2)

你想這次總該得到 2 了吧?結(jié)果,出錯(cuò)了:

 
 
 
  1. Debugger entered--Lisp error: (void-variable x)
  2.   (* x y)
  3.   (lambda (y) (* x y))(2)
  4.   funcall((lambda (y) (* x y)) 2)
  5.   eval_r((funcall f 2) nil)
  6.   eval-last-sexp-1(nil)
  7.   eval-last-sexp(nil)
  8.   call-interactively(eval-last-sexp nil nil)

看到問(wèn)題了嗎?f 的行為,隨著調(diào)用位置的一個(gè)“名叫 x”的變量的值而發(fā)生變化。而這個(gè) x,跟 f 定義處的 x 其實(shí)根本就不是同一個(gè)變量,它們只不過(guò)名字相同而已。這會(huì)導(dǎo)致非常難以發(fā)現(xiàn)的錯(cuò)誤,也就是早期的 Lisp 最令人頭痛的地方。我的老師 Dan Friedman 當(dāng)年就為此痛苦了很多年,直到 Scheme 的出現(xiàn),他才歡呼道:“終于有人把它給做對(duì)了!”

(附帶說(shuō)一句,Scheme 不是 Dan Friedman 發(fā)明的,而是 Guy Steele 和 Gerald Sussman。然而,F(xiàn)riedman 對(duì)程序語(yǔ)言的本質(zhì)理解,其實(shí)超越了 Lisp 的范疇,并且對(duì) Scheme 的后期設(shè)計(jì)做出了重要的貢獻(xiàn)。以至于 Sussman 在 Friedman 的 60 大壽時(shí)發(fā)表演說(shuō),戲稱(chēng)自己比起 Friedman 來(lái),“只是 Scheme 的用戶”。)

好在現(xiàn)在的大部分語(yǔ)言其實(shí)已經(jīng)吸取了這個(gè)教訓(xùn),所以你不再會(huì)遇到這種讓人發(fā)瘋的痛苦。不管是 Scheme, Common Lisp, Haskell, OCaml, Python, JavaScript…… 都不使用 dynamic scoping。

那現(xiàn)在也許你了解了,什么是讓人深?lèi)和唇^的 dynamic scoping。如果我告訴你,Lisp Machine 所使用的語(yǔ)言 ZetaLisp(也叫 Lisp Machine Lisp)使用的也是 dynamic scoping,你也許就明白了為什么 Lisp Machine 會(huì)失敗。因?yàn)樗F(xiàn)在的 Common Lisp 和 Scheme,真的是天壤之別。我寧愿寫(xiě) C++,Java 或者 Python,也不愿意寫(xiě) ZetaLisp 或者 Emacs Lisp。

話說(shuō)回來(lái),為什么早期的 Lisp 會(huì)使用 dynamic scoping 呢?其實(shí)這根本就不是一個(gè)有意的“設(shè)計(jì)”,而是一個(gè)無(wú)意的“巧合”。你幾乎什么都不用做,它就成那個(gè)樣子了。這不是開(kāi)玩笑,如果你在 emacs 里面顯示 f 的值,它會(huì)打印出:

 
 
 
  1. '(lambda (y) (* x y))

這說(shuō)明 f 的值其實(shí)是一個(gè) S 表達(dá)式,而不是像 Scheme 一樣的“閉包”(closure)。原來(lái),Emacs Lisp 直接把函數(shù)定義處的 S 表達(dá)式 ‘(lambda (y) (* x y)) 作為了函數(shù)的“值”,這是一種很幼稚的做法。如果你是***次實(shí)現(xiàn)函數(shù)式語(yǔ)言的新手,很有可能就會(huì)這樣做。Lisp 的設(shè)計(jì)者當(dāng)年也是這樣的情況。

簡(jiǎn)單倒是簡(jiǎn)單,麻煩事接著就來(lái)了。調(diào)用 f 的時(shí)候,比如 (funcall f 2),y 的值當(dāng)然來(lái)自參數(shù) 2,可是 x 的值是多少呢?答案是:不知道!不知道怎么辦?到“外層環(huán)境”去找唄,看到哪個(gè)就用哪個(gè),看不到就報(bào)錯(cuò)。所以你就看到了之前出現(xiàn)的現(xiàn)象,函數(shù)的行為隨著一個(gè)完全無(wú)關(guān)的變量而變化。如果你單獨(dú)調(diào)用 (funcall f 2) 就會(huì)因?yàn)檎也坏?x 的值而出錯(cuò)。

那么正確的實(shí)現(xiàn)函數(shù)的做法是什么呢?是制造“閉包”(closure)。這也就是 Scheme,Common Lisp 以及 Python,C# 的做法。在函數(shù)定義被解釋或者編譯的時(shí)候,當(dāng)時(shí)的自由變量(比如 x)的值,會(huì)跟函數(shù)的代碼綁在一起,被放進(jìn)一種叫做“閉包”的結(jié)構(gòu)里。比如上面的函數(shù),就可以表示成這個(gè)樣子:(Closure '(lambda (y) (* x y)) '((x . 1)))。

在這里我用 (Closure ...) 表示一個(gè)“結(jié)構(gòu)”(就像 C 語(yǔ)言的 struct)。它的***個(gè)部分,是這個(gè)函數(shù)的定義。第二個(gè)部分是 '((x . 1)),它是一個(gè)“環(huán)境”,其實(shí)就是一個(gè)從變量到值的映射(map)。利用這個(gè)映射,我們記住函數(shù)定義處的那個(gè) x 的值,而不是在調(diào)用的時(shí)候才去瞎找。

我不想在這里深入細(xì)節(jié)。如果你對(duì)實(shí)現(xiàn)語(yǔ)言感興趣的話,可以參考我的另一篇博文《怎樣寫(xiě)一個(gè)解釋器》。它教你如何實(shí)現(xiàn)一個(gè)正確的,沒(méi)有以上毛病的解釋器。

與 dynamic scoping 相對(duì)的就是“l(fā)exical scoping”。我剛才告訴你的閉包,就是 lexical scoping 的實(shí)現(xiàn)方法。***個(gè)實(shí)現(xiàn) lexical scoping 的語(yǔ)言,其實(shí)不是 Lisp 家族的,而是 Algol 60。“Algol”之所以叫這名字,是因?yàn)樗脑O(shè)計(jì)初衷是用來(lái)實(shí)現(xiàn)算法(algorithm)。其實(shí) Algol 比起 Lisp 有很多不足,但在 lexical scoping 這一點(diǎn)上它卻做對(duì)了。Scheme 從 Algol 60 身上學(xué)到了 lexical scoping,成為了***個(gè)使用 lexical scoping 的“Lisp 方言”。9 年之后,Lisp 家族的“集大成者” Common Lisp 誕生了,它也采用了 lexical scoping??磥?lái)英雄所見(jiàn)略同。

你也許發(fā)現(xiàn)了,Lisp 其實(shí)不是一種語(yǔ)言,而是很多種語(yǔ)言。這些被人叫做“Lisp 家族”的語(yǔ)言,其實(shí)共同點(diǎn)只是它們的“語(yǔ)法”:它們都是基于 S 表達(dá)式。如果你因此對(duì)它們同樣贊美的話,那么你贊美的其實(shí)只是 S 表達(dá)式,而不是這些語(yǔ)言本身。因?yàn)橐粋€(gè)語(yǔ)言的本質(zhì)應(yīng)該是由它的語(yǔ)義決定的,而跟語(yǔ)法沒(méi)有很大關(guān)系。你甚至可以給同一種語(yǔ)言設(shè)計(jì)多種不同的語(yǔ)法,而不改變這語(yǔ)言的本質(zhì)。比如,我曾經(jīng)給 TeX 設(shè)計(jì)了 Lisp 的語(yǔ)法,我把它叫做 SchTeX(Scheme + TeX)。SchTeX 的文件看起來(lái)是這個(gè)樣子:

 
 
 
  1. (documentclass article (11pt))
  2. (document
  3.   (abstract (...))
  4.   (section (First Section)
  5.       ... )
  6.   (section (Second Section)
  7.       ... )
  8. )

很明顯,雖然這看起來(lái)像是 Scheme,本質(zhì)卻仍然是 TeX。

所以,因?yàn)?Scheme 的語(yǔ)法使用 S 表達(dá)式,就把 Scheme 叫做 Lisp 的“方言”,其實(shí)是不大準(zhǔn)確的做法。Scheme 和 Emacs Lisp,Common Lisp 其實(shí)是三種不同的語(yǔ)言。Racket 曾經(jīng)叫做 PLT Scheme,但是它跟 Scheme 的區(qū)別日益增加,以至于現(xiàn)在 PLT 把它改名叫 Racket。這是有他們的道理的。

所以,你也許明白了為什么這篇文章的標(biāo)題叫做“Lisp 已死,Lisp 萬(wàn)歲!” 因?yàn)檫@句話里面的兩個(gè) “Lisp”其實(shí)是完全不同的語(yǔ)言?!癓isp 已死”,其實(shí)是說(shuō) ZetaLisp 這樣的 Lisp,由于嚴(yán)重的設(shè)計(jì)問(wèn)題,已經(jīng)死去。而“Lisp 萬(wàn)歲”,是說(shuō)像 Scheme,Common Lisp 這樣的 Lisp,還會(huì)繼續(xù)存在。它們先進(jìn)于其它語(yǔ)言的地方,也會(huì)更多的被借鑒,被發(fā)揚(yáng)廣大。

(其實(shí)老 Lisp 的死去還有另外一個(gè)重要的原因,那就是因?yàn)樵缙诘?Lisp 編譯器生成的代碼效率非常低下。這個(gè)問(wèn)題我留到下一篇博文再講。)


本文標(biāo)題:Lisp已死,Lisp萬(wàn)歲!
文章轉(zhuǎn)載:http://m.5511xx.com/article/djoeosp.html