Lisp: 我不是爲了 (zhuang) 宏(bi),我只是爲了(ke)讀(du)取(xing)

from http://jlongster.com/Lisp--It-s-Not-About-Macros,-It-s-About-Readhtml

注意:這兒的例子僅僅在 https://github.com/jlongster/outlet 有效,請參考你熟悉的 lisp/scheme 版本中的 read (或者叫其餘的什麼 forms ) 相關文檔。java

我知道這是挖墳,可是此文 http://kuomarc.wordpress.com/2012/02/02/why-i-love-common-lisp-and-hate-java-part-ii-code-examples/ 帶了錯誤的路。其中宏的使用很是使人困惑,用函數會更好點。做者認可了這點,可是他依然這麼幹了,並且 https://kuomarc.wordpress.com/2012/02/13/why-i-love-common-lisp-and-hate-java-part-iii-macros/ 這篇文章更多的關注了宏,可是依然沒有突出重點。python

真理是:真的不是爲了(zhuang)宏(bi)而宏的。git

Lisp 的優點更長遠。讓咱們中止把宏往人臉上糊(而且說宏是惟一更好的抽象方式)。那些人想不通是由於這須要親自體會。若是隻用普通的函數就很合適的狀況下還用宏,就更讓人困惑了。"Why do you need a macro(宏)? I can abstract the same thing with a function!"github

我不是爲了 (zhuang) 宏(bi),我只是爲了(ke)讀(du)取(xing)web

read 是一個內置的讀取對象的函數,在大多數的 lisp 實現中。對象能夠是任何原子(數字,字符串,等等),或者一個數據結構好比 list,下面是例子:數據結構

(read "3") ; 3
(read "foo") ; '>>> 'foo
(read "\"foo\"") ; "foo"
(read "(foo bar baz)") ; (list 'foo 'bar 'baz)

稍等,看看 lisp 的代碼,他真的都是 list 組成的:編輯器

(define (foo x)
  (+ x 1))

一旦你理解了 lisp 代碼就是數據 (即:atom 列表),那麼你懂了,你能用 read in 來讀取 lisp 代碼做爲數據。因爲 lisp 提供了一組函數,優雅的處理列表,解析 lisp 代碼忽然就變得很簡單了。wordpress

舉個栗子:函數

;; program.lisp

(define (foo x)
  (+ x 1))

;; our parser

;; get-file-contents returns a file's contents as a string
;; (defined elsewhere)
(define src (get-file-contents "program.lisp"))

;; src is a string, turn it into data
(define forms (read src))

;; `car` retrieves the first item of a list
(display (car forms)) ; 'define

;; `cons` puts an item at the beginning of the list
;; `cdr` gets the rest of the list after the first item
;; `cadr` is a combination, equal to (car (cdr x))
;; `cddr` is a combination, equal to (cdr (cdr x))
(let ((name-args (cadr forms)))
  (cons 'lambda
        (cons (cdr name-args)
              (cddr forms))))

;; output: (lambda (x) (+ x 1))

咱們這就寫了個把全部的 define 表達式轉換爲 lambda 表達式。

這應該是另人印象深入的。python js 你怎麼作?你須要轉換成 ast ,還要學習不少內置方法來解析他。在 lisp 代碼就是 ast 結構的數據。

把 read 當作 js 中的 JSON.parse ,js 代碼和數據是不一樣的, JSON.parse 也不能解析全部的東西。在 lisp 他們都是同樣的。

宏到底是啥?它只爲了讀取打包的漂漂亮亮的數據到正式系統中。你能在編譯器上安裝宏功能。編譯器把程序做爲數據讀入,用解析並查找宏調用的方式展開他。當找到一個宏調用,編譯器把程序做爲數據傳給宏,這時你能夠對程序作任何事情。而後你返回一些數據給編譯器,做爲代碼來解析執行。

若是你依然沒被說服,這裏,你能寫一個 30 行的 lisp 來實現一個宏系統 https://gist.github.com/1712455,你只須要 read ,而且它很簡單。

因此這有什麼意義呢

我不想這篇文章變成「Lisp 真是太酷啦」類型的又一篇文章,每一個語言都有他本身的益處,選擇你喜歡的。

我只想解釋清楚,爲何 lisp 族人呼籲宏。他基於 lisp 代碼即數據的事實,象徵着更強大的一種能力。若是你真的對此好奇,我鼓勵你跟隨所想,你會發現你想知道的東西。

一些例子,有這些功能的話那麼:調試工具變得很容易寫(好比一個 40行 tracer https://gist.github.com/1840230 )單元測試能追蹤到失敗的原始表達式,因此它能展現出錯誤背後真正所發生的狀況。我很難展現出這特性所包含的全部潛力。

我喜歡Paul Graham的文章 「超越平凡」 http://www.paulgraham.com/avg.html 下的這個評論:

Viaweb 編輯器的源代碼,可能有 20 - 25%的宏。宏比普通的 lisp 函數難寫,並且在不必使用宏的地方,宏被視做很差的代碼風格。因此每一個宏都是不得不用的時候才寫的。也就是說至少 20 - 25% 的代碼功能很難用任何別的語言輕易的作到的。不管如何,炮灰猴子可能依然懷疑這神祕的 lisp 能力,這應該會讓他感到很古怪。

若是函數比宏更合適,不要考慮這麼小的尺度。在更大的尺度上考慮,若是你能寫一個解析器來對你的代碼作一些瘋狂的必要的事情,也許這隻要 50 行代碼,這是別的語言沒法作到的。

相關文章
相關標籤/搜索