偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Lisp已死,Lisp萬歲!

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

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

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

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

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

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

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

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

早期的 Lisp 其實(shí)普遍存在一個(gè)非常嚴(yán)重的問題:它使用 dynamic scoping。所謂 dynamic scoping 就是說,如果你的函數(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 對于函數(shù) (lambda (y) (* x y)) 來說是個(gè)“自由變量”(free variable),因?yàn)樗皇撬膮?shù)

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

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

如果我們在函數(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。

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

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

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

再來。如果我們直接調(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) 

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

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

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

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

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

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

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

簡單倒是簡單,麻煩事接著就來了。調(diào)用 f 的時(shí)候,比如 (funcall f 2),y 的值當(dāng)然來自參數(shù) 2,可是 x 的值是多少呢?答案是:不知道!不知道怎么辦?到“外層環(huán)境”去找唄,看到哪個(gè)就用哪個(gè),看不到就報(bào)錯(cuò)。所以你就看到了之前出現(xiàn)的現(xiàn)象,函數(shù)的行為隨著一個(gè)完全無關(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 語言的 struct)。它的***個(gè)部分,是這個(gè)函數(shù)的定義。第二個(gè)部分是 '((x . 1)),它是一個(gè)“環(huán)境”,其實(shí)就是一個(gè)從變量到值的映射(map)。利用這個(gè)映射,我們記住函數(shù)定義處的那個(gè) x 的值,而不是在調(diào)用的時(shí)候才去瞎找。

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

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

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

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

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

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

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

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

原文鏈接:http://blog.sina.com.cn/s/blog_5d90e82f0101jo6r.html

責(zé)任編輯:林師授 來源: 王垠的博客
相關(guān)推薦

2024-10-15 11:16:18

2019-10-11 08:58:21

Hadoop開源

2015-04-23 09:34:18

CoffeeScripJavaScript開

2013-02-26 11:01:42

CIO信息化大數(shù)據(jù)云計(jì)算

2010-04-06 09:02:59

Solaris甲骨文Sun

2024-12-10 09:07:17

2015-11-05 10:17:42

網(wǎng)絡(luò)工程師IT人員

2018-01-08 07:58:51

深度學(xué)習(xí)編程人工智能

2025-01-13 08:20:00

AI模型訓(xùn)練

2012-06-04 10:34:17

Lisp

2009-09-03 18:32:43

Lisp函數(shù)

2017-03-16 09:30:56

LispAI數(shù)據(jù)結(jié)構(gòu)

2017-01-05 13:31:33

Lisp加法運(yùn)算

2012-11-22 10:11:16

LispLisp教程

2011-04-15 09:23:33

IETFLISP路由器

2014-12-24 13:53:48

2015-05-20 09:05:40

2011-10-14 09:20:48

Lisp

2013-03-18 09:30:18

Lisp

2010-10-15 10:35:18

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)