Lisp的精髓是什麼?

經常在網上見到有人推薦別人學Lisp,說法大概都是學會了Lisp的思想後對於自己的編程能力大有幫助。

因此我想問下Lisp的精髓是什麼?為何它實際應用範圍不廣(相對於C/C++/Java 及Perl/Python/PHP),但卻受到較高的評價?


人肉語法樹


lisp 模糊了過程與數據,數據與結構之間的邊界。這樣,結構和過程可以是數據,數據也可以是結構和過程。這種抽象非常符合事物的本質,所以用lisp去描述映射現實事物的時候,能夠很自然和思維認知保持一致。

如果說,編程語言的語法是工具,讓人去抽象構建現實世界。那麼lisp的語法就是量子,讓人去構建物質,然後在迭代構建其它的一切。整個語言就是一個遞歸。


正好本學期修過學院開的SICP課程,也寫過一些解釋器的擴展.
其實它的大多數特性像高階函數、惰性求值和函數閉包什麼的,很多語言的現代標準里都已經吸收了,所以當時看上去很稀奇的東西現在不新鮮了.


個人覺得比較精髓的是Code as data,然後運算都是List.所以想擴展語法糖和寫解釋器非常方便!

現在看Lisp(Scheme)更適合寫寫解釋器把玩把玩,把現代Python/C++/Java的語法標準拿到20年前看上去也是非常酷的.

人生苦短,我用Python.還是早點寫完代碼,去打望妹子比較開心.


過濾人

(逃


我覺得lisp的精髓在於直接編寫抽象語法樹,宏的目的只是簡化這個工作而已。

再強調一下,我沒完整學過lisp語言,沒有用lisp寫過一行代碼,也沒有使用emacs的習慣,上面的說法只是一個外行的扯淡而已


我有Emacs Lisp經驗,其他主流的語言都做過商業開發,但不是語言專家(比如Macro能理解,但從未使用過),所以只能從實用角度來談談.

就Emacs Lisp來說,我覺得比較獨特的特性是autoload和defadvice.

autoload可以在用戶實際用到某個api的時候動態載入其所在模塊. 比如我寫了個支持上百種語言的插件,用戶並不需要把對應每種語言的子模塊在初始化時載入內存.這樣我的插件響應快,消耗內存少.

和java/c++語言比,缺點是系統不穩定,因動態載入也可能因為內存不足而失敗,這對需要長時間穩定運行的系統是不可接受的.

defadvice可在運行時刻改變第三方模塊的行為,對文本編輯器很有用,假設第三方插件A有一段代碼在最新版的Emacs中拋出異常,你可以在不碰插件A的代碼的情況下用你自己的實現注入A,完全改變A的行為..

這在大型系統也易遭到濫用,因為一個模塊的任何行為都能被模塊外的代碼改變.這樣根本無法調試.

至於說Lisp的語法表現力,我覺得除了macro外(沒用過macro不好評價)其他都比現代的動態語言javascript差一點.

所以如果瀏覽器同時支持Lisp和javascript,我會用javascript. 大型軟體開發至少需要保證最差程序員的代碼可靠地儘早地崩潰(最怕就是開發版本都好,上線一個月崩潰),或者其破壞性可以局限在某個模塊里. autoload和defadvice對於團隊開發絕對是災難.

我個人認為把Lisp局限於應用軟體Domain Specific Language是明智的.

作為編輯器的膠水語言, Lisp有以下優勢:
- 不尋常的語法對智商是一個過濾
- 語法糖特別適合對編輯器優化, 語言本身並沒有給你任何不可跨越的限制.
- Emacs配置本來就是個人行為.代碼量不會很大,都放到同一個目錄下grep就行了.所以也不存在多人開發導致質量下降的問題.

題外話,沒有自己思考和實踐,不要輕易相信或者排斥任何觀點.這些觀點的唯一作用就是提供了你一個看事物的新角度和調查的起點.

比如網上很多人說Emacs的文檔寫得很優美,又說Emacs的Gnus作為郵件客戶端已過時了.我花了一天的時間把Gnus的文檔和網上相關文章通讀了一遍,結論是Gnus的文檔作為用戶手冊寫得太糟糕,而軟體本身卻有一些非常優秀的killer feature,

我比較驚訝的是,關於Gnus的文章很多,但是幾乎都是淺嘗轍止,重要的特性幾乎都沒講,所以我寫了Gnus實用指南 (Practical guide to use Gnus with Gmail).


大道至簡。

別的語言一堆語法,Lisp只要一對括弧和一個符號名就表達了(S表達式)。

因為S表達式解析起來又簡單,它可以表達成數據,也可當作代碼,所以可以用宏。


因為其他所有語言都是 Lisp 的真子集口牙(逃


s表達式
不可變性
遞歸

我使用過clojure。

lisp的特點在於對計算過程的高度抽象,使得它在做任何計算的時候,代碼量都出奇的少。

但是對於狀態極多的系統,比如ui,lisp優勢就不大了。

現在什麼都是web的,因此lisp又開始火起來了,關鍵就在於其高效的計算能力,這一點用過都知道。


如果精髓指的是特有而強大的地方,那麼我認為LISP的精髓在於2點:

  1. Functional Programming
  2. Homoiconicity (同相性)

而且,第二點Homoiconicity是LISP的精髓中的精髓。因為,除了LISP之外很多語言都是支持Functional Programming,但是Homoiconicity只有LISP語言家族有。Code as Data!

有碼有真相:) 舉個LISP方言Clojure的例子來說明Homoiconicity:
大家都知道LISP是前綴表達式的:

(+ 1 2)
; =&> 3

這是語法級別的表達,放在其他語言就這麼定死了。但是在LISP裡面可以通過宏自己做一個中綴表達式求值:

(defmacro my-plus
[f] (list (second f) (first f) (last f)))

(my-plus (1 + 2))
; =&> 3

就這樣直接把表達式拿過來作處理了,酷!需要注意的是,這個宏不是直接操作字元串,而是正真操縱了表達式!這就是code as data。

感受到了酷炫了。問題是在LISP代碼的可讀性很差,這也是它不能在工業界流行的重要原因。它的學習價值大於它的使用價值,這也是很多人對它評價高的原因。
也許從未拿它實戰,但是學習它能增強您的碼力。

plus:
評論中 @swordfeng的小號 建議用解構(destructuring)來做:

(let [[one plus two] "(1 + 2)]
((eval plus) one two))

同理,但是更酷了!


Lisp 的精髓難道不是在一個最小的規則集上面用自洽的方式擼出所有的Feature ,然後的結果就是從理論上說,Feature 可以無限豐富。

方言的問題也就是因為這個精髓過於牛逼了,牛逼到摟不住了。現在很多好一點的新語言不都是在這個擼多擼少上面找平衡呢嗎?

再多啰嗦一句,這種牛逼的技能是從一開始選擇技術路線的時候就已經確定了的,另一條路上來的東西光靠後天往上摞庫是搞不定的。

哦,還有一半問題是為什麼實際應用的範圍不廣?

因為這種玩法的技術難度是很大的。因為他要求所有的一切必須是自洽的,所以這裡面沒有任何地方可以走捷徑。你不能說列出十幾個玩不轉的地方,這十幾個我在編譯器里單算,我不跟你們一個路子,我這個部分特殊對待。這樣當然有可能減小難度了,但是這整個兒體系你就不能選這條路了。


了各位摸 哥的回答,我來總結一下:LisP的精髓就是:
(you-know-nothing?
"(lisp-of-lisp)
(comment "said by J.S"))


code is data


唯一的優點,是它可藉助任意時刻的某一步結論,無限制利用它的全部邏輯工具推理遞歸和解決下一階邏輯,其他的都是細枝末節。

lisp的本質是近似的無限制eval的一種簡潔但未必是最優的實現方式。何況宏如果被編譯,其邏輯價值只是eval的子集。

可eval自身的語言必然強於不可eval自身的語言,只返回結果不產生副作用的程序不能超前預知自身結果,因為那樣就不需要執行。如果程序自身的某種不可動態構造的結構依賴於動態執行結果,則無eval能力的程序在不fuck出這種結構的等價品之前肯定無能為力。比如程序可以根據當前可預測邏輯條件只保留需要執行的分支,從而比遍歷分支做邏輯判斷具有更少的執行步驟。所以要麼程序所有結構全可動態產生,要麼給我eval的權利,要麼程序本身產物不干涉程序自身(比如HTML),否則通常程序都會對於第一推動力產生更多的浪費。

拋棄eval可能強烈受困於一階邏輯的不自恰性和不完備性。類似於理髮師悖論。

函數不是遞歸的充分必要條件,函數對象也只是可重複代碼塊的引用機制之一而不是唯一,gc的存在以及邏輯的關聯性導致大量複雜引用的大體積代碼數據,lazy load更是容易造成事成之後若干時間才出現的悲慘的性能損耗,函數對象和閉包不是純的代碼塊,經常做一些邪惡而不可忽視的骯髒的事情。

eval也不是完美避免理髮師悖論的方式,畢竟動態執行除了解析抽象語法樹外,還有些優化就沒了,比如js的作用域優化。寫直接生成源代碼文件的源代碼文件的源代碼。。。的遞歸,一般也可以達到無視語言高級特性,無視eval優化能力不足問題,媲美人肉代碼效率,接近有限遞歸eval的邏輯能力的效果。

計算機本身特別愚蠢,計算機的任何自作聰明都具有驚人的代價,這並不是你換門外語可以改觀的事實。

lisp的先進性類似於詭辯,你沒法證明也沒法否認,且有太多遞歸折磨腦細胞的無價值細枝末節。


我覺得中綴運算符表達式要是都被()包圍,一樣能做到無歧義啊——


剛學了一遍,準備入坑擴展自己的emacs。目前看來,主流語言基本多多少少都涵蓋了lisp的特性。要說比較特別的特性,估計就是宏和符號表達式。

應用範圍不廣,因為比較特別的這兩個特性,太過於靈活。工程的話,由於人員水平差距,不好控制。現在有的程序員連個非同步寫起來都暈頭轉向的,如果用上宏加入大量函數式,一遍構建語言一遍構建框架的方式工作,估計他們基本很難寫出像樣的代碼。

lisp的宏非常可怕,我本人看的時候,不藉助macroexpand函數,基本得很久才能看懂這個宏在幹嘛。一個宏裡面,quote之類的就要理解一陣子,再加上寫的時候基本上全是函數式的方式,一會quote一會unquote,你還要注意防止使用intern變數,防止多重求值,簡直就是眼花繚亂。

評價較高因為難吧。實用不是用,則很難說,取決於具體工作。就像我覺得很多數學難得過頭一樣。不過lisp比起數學來說,還是溫柔不少的。

提高實戰水平的途徑不是學語言,是看代碼,和寫代碼。lisp只能給你一些以前你沒想到的啟發。以上。


至少是智商過濾器,學了之後腦力會增強


Lisp is a powerful language to express ideas. Maybe the most.
個人認為精髓在於遞歸,雖然其他語言也有,然而遞歸+s表達式就一切不同了。


我覺得別人推薦的主要是scheme language和sicp這本書…

其對應的課是MIT 6.001(已下架)。

話說MIT為什麼要學生學scheme呢?因為它語法簡單,學生的注意力可以完全放在數據結構和各種抽象上。以至於這門課上完就能寫個scheme解釋器。這種「古典作風」絕對有助於培養理論科學家。(當然作為歷史悠久的人工智慧初期的語言並以lisp machine佔據cs山頭一角,其本身的影響力也不容小覷)。

話說MIT放棄這門課,改用python其實也挺有道理的。
一來scheme效率低下,也不方便做工程。
二來畢竟現代社會並不需要那麼多從頭造輪子的人,大量工作已經離不開開源項目。從現實角度考慮工程實現難度和本科學生的學習曲線來看,python相當合適。你不妨也從python學起。

人生苦短,我有python。


精髓就是括弧也就是S表達式。
S表達式的閉包特性,LISP中的list數據結構跟S表達式語法結構極為相像,導致LISP用程序寫程序的能力太強大了。


LISP的精髓不是括弧嗎。。。。

以下為答案本體:
LISP是基於lambda演算的函數式語言。個人認為它的精髓在於它可以用七個公理組合變化出眾多的計算過程,而大多數編程語言的也是這樣,尤其scheme這種非常簡潔的方言,可以讓人更加關注如何抽象一個問題只用最簡單的幾種操作構建出問題的解。


推薦閱讀:

TAG:編程語言 | 函數式編程 | Lisp | 編程語言比較 |