原文見 『Emergency Elisp』。html
你用着 Emacs 卻不懂 Lisp 吧?歡迎閱讀這篇 Emacs Lisp 入門教程!它應該可以助你搞定 Emacs Lisp,從而更加自如的駕馭 Emacs。編程
有不少種學習 Lisp 的方式,其中有一些方式要比其餘方式更爲 Lisp。我喜歡的方式是,基於 C++ 或 Java 的編程經驗來學習 Lisp。數組
本文重點放在 Emacs Lisp 語言自己,由於它纔是最難的部分,至於成噸的 Emacs 的 API 的用法,你能夠經過閱讀 Emacs Lisp 文檔來學習。網絡
有些事(例如編寫生成代碼的代碼)是 Lisp 擅長的,而有些事(例如算數表達式)是它不擅長的。我不打算談論 Lisp 是好仍是壞,只關心如何用它編程。Emacs Lisp 跟其餘語言差很少,最終你會習慣它的。數據結構
許多介紹 Lisp 的文章或書籍嘗試給你展示 Lisp 之『道』,飽含着奉承、讚頌以及瑜伽之類的東西。事實上,一開始我真正想要的是一本簡單的 cookbook,它講述的是如何用 Lisp 來作一些我平常生活中的事。本文便立意於此,它講述的是大體是如何用 Emacs Lisp 來寫 C,Java 或 JavaScript 就能寫的那些代碼。app
咱們開始吧,看看我可以將這篇文章寫的多麼短小。我要從挺無聊的詞法標記、運算符開始,而後講述如何實現一些衆所周知的語句、聲明以及一些程序結構。less
Lisp 代碼是像 (+ 2 3)
的嵌套的括號表達式。這些表達式有時被稱爲 form(塊)。編程語言
也有些不帶括號的代碼,譬如字符串、數字、符號(必須以單引號爲前綴,例如 'foo)、向量等,它們被稱爲原子(基本上可理解爲葉結點)。函數
註釋只能是單行的,分號是註釋符。oop
要將一個名爲 foo
的變量的值設置爲 "bar"
,只需:
(setq foo "bar") ; setq means "set quoted"
要以 "flim"
與 "flam"
做爲參數值調用一個名爲 foo-bar
的函數,只需:
(foo-bar "flim" "flam")
要進行算 (0x15 * (8.2 + (7 << 3))) % 2
,只需:
(% (* #x15 (+ 8.2 (lsh 7 3))) 2)
也就是說,Lisp 的算數運算用的是前綴表達式,與 Lisp 函數調用方式一致。
Lisp 沒有靜態類型系統;你能夠在程序運行時判斷數據的類型。在 Emacs Lisp 中,謂詞函數一般以 p
做爲後綴,其含義下文有講。
重點:能夠在 Emacs 的 *scratch*
緩衝區中對 Emacs Lisp 表達式進行求值試驗,有如下幾種基本的求值方式:
將光標移到表達式最後一個封閉的括號的後面,而後執行 C-j
(即 Ctrl + j 鍵);
將光標移到表達式內部,而後執行 M-C-x
(即 Alt + Ctrl + x 鍵);
將光標放到表達式最後一個封閉的括號的後面,而後執行 C-x C-e
。
第一種求值方式會將求值結果顯示於 *scratch*
緩衝區,其餘兩種方式會將求值結果顯示於 Emacs 的小緩衝區(Minibuffer)。這些求值方式也適用於 Lisp 的原子——數字、字符串、字符以及符號。
Lisp 的詞法標記(原子級別的程序元素)屈指可數。
註釋是單行的,由分號領起:
(blah blah blah) ; I am a comment
帶雙引號的就是字符串:
"He's said: \"Emacs Rules\" one time too many."
要讓字符串含有換行符,只需:
"Oh Argentina! Your little tin of pink meat Soars o'er the Pampas"
?
x 能夠得到字符 x 的 ASCII 碼,這裏的 x 能夠是任意 ASCII 編碼的字符。例如 ?a
的求值結果是 ASCII 碼 97
,而 ?
(問號後面是一個空格)的求知結果是 32。
?
後面尾隨的字符,有些須要逃逸,例如 ?\(
,?\)
以及 ?\\
。
Emacs 22+ 支持 Unicode,這超出了本文範圍。
字符本質上只是整型數值,所以你能夠對它們作算術運算(例如,從 ?a
迭代到 ?z
)。
整型數的位數是 29 位(並不是你們習慣的 32 位);
二進制數,前綴是 #b
,例如 #b10010110
;
八進制數:#o[0-7]+
,例如 #o377
;
十六進制數,前綴是 #x
,例如 #xabcd
,xDEADBEE
;
浮點數:位數是 64;
科學計數,例如 5e-10
,6.02e23
。
在不支持大整數的 Emacs Lisp 中,變量 most-positive-fixnum
與 most-negative-fixnum
分別是最大的與最小的整型數。Emacs 22+ 提供了一個叫作 calc
的大整數/數學庫,以備不時之需。也就是說,Emas Lisp 的算數運算會發生上溢和下溢,如同你在 C 或 Java 中遇到的狀況類似。
符號 t
是 true
,符號 nil
是 false
(與 null
同義)。
在 Emacs Lisp 中,nil
是惟一的『假』值,其餘非 nil
值皆爲『真』值,也就是說像空字串、0、'false
符號以及空向量之類,都是真值。不過,空的列表 '()
與 nil
等價。
Emacs Lisp 有定長數組,名曰『向量』(Vector)。可以使用方括號來構建預先初始化的字面向量,例如:
[-2 0 2 4 6 8 10] ["No" "Sir" "I" "am" "a" "real" "horse"] ["hi" 22 120 89.6 2748 [3 "a"]]
注意,要使用空白字符來隔離數組中的元素,不要使用逗號。
向量中存儲的數據能夠是混合類型,也可以對向量進行嵌套。一般是使用 make-vector
來構建向量,由於字面向量是單例,對此不要驚訝。
Lisp 重度依賴鏈表,所以專門爲它提供了詞法標記。圓括號裏的任何東西都是列表,除非你引用了它,不然 Lisp 解釋器就會像函數調用那樣對其進行求值。在 Lisp 中有如下幾種列表引用形式:
(quote (1 2 3)) ; 產生列表 (1 2 3),而且不會對列表元素進行求值 '(1 2 3) ; 單引號是 (quote (...)) 形式的簡寫,注意它在左括號以外 (list 1 (+ 1 1) 3) ; 也能夠產生列表 (1 2 3),由於 Lisp 解釋器會首先對列表元素進行求值 `(1 ,(+ 1 1) 3) ; 也能夠產生列表 (1 2 3),這是通過『反引號』模板系統產生的
關於列表還有不少東西可說,可是其餘人已經都說過了。
你能夠直接設定 Lisp 列表的首部與尾部,將其做爲 2 個元素的無類型結構來使用。語法是 (head-val . tail-value)
,不過必須是引用的形式(見上文)。
對於較小的數據集,檢索表的數據結構一般設計爲關聯列表(即所謂的 alist
),這只不過是帶點的序對所構成的列表而已,例如:
'( (apple . "red") (banana . "yellow") (orange . "orange") )
Emacs Lisp 有內建的哈希表,位向量等數據結構,可是它們並無語法,你只能經過函數來建立它們。
有些運算,在其餘語言中體現爲運算符的形式,而在 Emacs Lisp 中體現爲函數的調用。
數值相等判斷:(= 2 (+ 1 1))
,單個等號,求值結果爲 t
或 nil
,也能用於浮點數比較。
數值不相等判斷:(/= 2 3)
,看上去像相除後賦值,但並非。
值相等判斷:(eq 'foo 2)
,相似於 Java 的 ==
,適用於整型、符號、限定字串(Interned String)以及對象引用的相等比較。對於浮點數,可以使用 eql
(或者 =
)。
結構的深度相等比較:使用 equal
,例如:
(equal '(1 2 (3 4)) (list 1 2 (list 3 (* 2 2)))) ; 求值結果爲 t
equal
函數相似於 Java 的 Object.equals()
,適用於列表、向量、字符串等類型。
字符串沒有任何運算符,只是有不少字符串操做函數,下面是幾個經常使用的函數:
(concat "foo" "bar" "baz") ; 求值結果爲 "foobarbaz" (string= "foo" "baz") ; 求值結果爲 nil (false),也能夠用 equal (substring "foobar" 0 3) ; 求值結果爲 "foo" (upcase "foobar") ; 求值結果爲 "FOOBAR"
使用 M-x apropos RET \bstring\b RET
可查看全部與字符串操做相關的函數說明。
仍是畫個表容易看……
這一節會給出一些相似 Java 語句的代碼片斷。它不復雜,僅僅是讓你可以上手的方子。
狀況 1:無 else
從句((if test-expr expr)
)
示例:
(if (>= 3 2) (message "hello there"))
狀況 2:else
從句((if test-expr then-expr else-expr)
)
(if (today-is-friday) ; test-expr (message "yay, friday") ; then-expr (message "boo, other day")) ; else-expr
若是你須要在 then-expr
中存在多條表達式,可以使用 progn
——相似於 C 或 Java 的花括號,對這些表達式進行封裝:
(if (zerop 0) (progn (do-something) (do-something-else) (etc-etc-etc)))
在 else-expr
中不必使用 progn
,由於 then-expr
以後的全部東西都被視爲 else-expr
的一部分,例如:
(if (today-is-friday) (message "yay, friday") (message "not friday!") (non-friday-stuff) (more-non-friday-stuff))
狀況 3: 經過 if
語句的嵌套可實現 else-if
從句,也能夠用 cond
(下文有講):
(if 'sunday (message "sunday!") ; then-expr (if 'saturday ; else-if (message "saturday!") ; next then-expr (message ("weekday!")))) ; final else
狀況 4:無 else-if
的多分支表達式——使用 when
:
若是沒有 else
從句,可使用 when
,這是一個宏,它提供了隱式的 progn
:
(when (> 5 1) (blah) (blah-blah) (blah blah blah))
也能夠用 unless
,它的測試表達式與 when
反義:
(unless (weekend-p) (message "another day at work") (get-back-to-work))
經典的 switch
語句,Emacs Lisp 有兩個版本:cond
與 case
。
Emacs Lisp 的 cond
與 case
不具有 switch
的查表優化功能,它們本質上是嵌套的 if-then-else
從句。不過,若是你有多重嵌套,用 cond
或 case
要比 if
表達式更美觀一些。cond
的語法以下:
(cond (test-1 do-stuff-1) (test-2 do-stuff-2) ... (t do-default-stuff))
do-stuff
部分能夠是任意數量的語句,無需用 progn
封裝。
與經典的 switch
不一樣,cond
能夠處理任何測試表達式(它只是依序檢驗這些表達式),並不是僅限於數字。這樣所帶來的負面影響是,cond
對數字不進行任何特定的轉換,所以你不得不將它們與某種東西進行比較。下面是字符串比較的示例:
(cond ((equal value "foo") ; case #1 – notice it's a function call to `equal' so it's in parens (message "got foo") ; action 1 (+ 2 2)) ; return value for case 1 ((equal value "bar") ; case #2 – also a function call (to `+') nil) ; return value for case 2 (t ; default case – not a function call, just literal true 'hello)) ; return symbol 'hello
末尾的 t
從句是可選的。若某個從句匹配成功,那麼這個從句的求值結果即是整個 cond
表達式的求值結果。
Emacs 'cl
(Common Lisp)包(譯註:Emacs Lisp 手冊推薦使用 'cl-lib
,由於 'cl
過期了),提供了 case
,它可以進行數值或符號比較,所以它看上去比較像標準的 switch
:
(case 12 (5 "five") (1 "one") (12 "twelve") (otherwise "I only know five, one and twelve.")) ; result: "twelve"
使用 case
,默認從句能夠用 t
,也能夠用 otherwise
,但它必須最後出現。
使用 case
更乾淨一些,可是 cond
更通用。
Emacs Lisp 的 while
函數相對正常一些,其語法爲 (while test body-forms)
。
例如,可在 *scratch*
緩衝區中執行如下代碼:
(setq x 10 total 0) (while (plusp x) ; 只要 x 是正數 (incf total x) ; total += x (decf x)) ; x -= 1
在上述代碼中,咱們首先設置了兩個全局變量 x=10
與 total=0
,而後執行循環。循環結束後,可對 total
進行求值,結果爲 55(從 1 到 10 求和結果)。
Lisp 的 cache/throw
可以實現控制流的向上級轉移,它與 Java 或 C++ 的異常處理類似,儘管功能上要弱一些。
在 Emacs Lisp 中要 break
一個循環,能夠將 (cache 'break ...)
置於循環外部,而後在循環內部須要中斷的地方放置 (throw 'break value)
,例如:
符號 'break
不是 Lisp 語法,而是本身取的名字——要取容易理解的名字,譬如對於多重循環,可在 cache
表達式中用 'break-outer
與 'break-inner
之類的名字。
若是你不關心 while
循環的『返回值』,能夠 (throw 'break nil)
。
要實現循環中的 continue
,可將 cache
置入循環內部之首。例如,對從 1 到 99 的整數求和,而且在該過程當中避開能被 5 整除的數(這是個蹩腳的例子,只是爲了演示 continue
的用法):
可將這些示例組合起來,在同一個循環內實現 break
與 continue
:
上面的循環的計算結果爲 4000,即 total
的值。要獲得這個結果,還有更好的計算方式,不過我須要足夠簡單的東西來說述如何在 Lisp 中實現 break
與 continue
。
catch/throw
機制可以像異常那樣跨函數使用。不過,它的設計並不是真的是面向異常或錯誤處理——Emacs Lisp 另外有一套機制來作這些事,也就是後文的 try/catch
這一節所討論東西。你應該習慣在 Emacs Lisp 代碼中使用 catch/throw
進行控制流轉移。
Emacs Lisp 中最容易使用的循環機制是 Common Lisp 包提供的 loop
宏。要使用這個宏,須要加載 cl-lib
包:
(require 'cl-lib) ; 獲取大量的 Common Lisp 裏的好東西
loop
宏是帶有大量特徵的微語言,值得好好觀摩一番。我主要用它來演示如何構造一些基本的循環。
基於 loop
所實現的 do/while
機制以下:
(loop do (setq x (1+ x)) while (< x 10))
在 do
與 while
之間能夠有任意數量的 Lisp 表達式。
C 風格的 for
循環由四種成分構成:變量初始化,循環體,條件測試以及自增。用 loop
宏也能模擬出這種循環結構。例如,像下面的 JavaScript 的循環結構:
var result = []; for (var i = 10, j = 0; j <= 10; i--, j += 2) { result.push(i+j); }
對於這樣的循環結構,基於 Emacs Lisp 的 loop
可將其模擬爲:
(loop with result = '() ; 初始化:只被執行一次 for i downfrom 10 ; i 從 10 遞減 for j from 0 by 2 ; j 從 0 開始自增 2 while (< j 10) ; j >= 10 時循環終止 do (push (+ i j) result) ; 將 i + j 的求值結果入棧 finally return (nreverse result)) ; 將 result 中存儲的數據次序逆轉,而後做爲求值結果
因爲 loop
表達式有不少選項,這樣寫雖然繁瑣,可是容易理解。
注意,上述代碼中,loop
聲明瞭一個數組 result,而後將它做爲『返回』值。事實上,loop
也能處理循環以外的變量,這種狀況下就不須要 finally return
從句了。
loop
宏出人意料的靈活。有關它的全面介紹超出了本文範疇,可是若是你想駕馭 Emacs Lisp,那麼你有必要花一些時間揣摩一下它。
若是你迭代訪問一個集合,Java 提供了『智能』的 for
循環,JavaScript 提供了 for .. in
與 for each .. in
。這些,在 Lisp 裏也能作到,可是你可能須要對 loop
宏有很好的理解,它能夠爲迭代過程提供一站式服務。
最基本的方式是 loop for var in sequence
,而後針對特定結果作一些處理。例如,你能夠將 sequence
中的東西收集起來(或者將一個函數做用與它們):
(loop for i in '(1 2 3 4 5 6) collect (* i i)) ; 結果爲 (1 4 9 16 25 36)
loop
宏可以迭代列表元素、列表單元、向量、哈希鍵序列、哈希值序列、緩衝區、窗口、窗框、符號以及你想遍歷的任何東西。請參閱 Emacs 手冊得到更多信息。
用 defun
(define function)定義函數。
語法:(defun 函數名 參數列表 [可選的文檔化註釋] 函數體)
(defun square (x) "Return X squared." (* x x))
對於無參函數,只需讓參數列表爲空便可:
(defun hello () "Print the string `hello' to the minibuffer." (message "hello!"))
函數體可由任意數量的表達式構成,函數的返回值是最後那個表達式的求值結果。因爲函數的返回類型沒有聲明,所以有必要在文檔化註釋中註明函數的返回類型。對函數進行求值以後,其文檔化註釋可經過 M-x describe-function
查看。
Emacs Lisp 不支持函數/方法的重載,可是它支持 Python 和 Ruby 所提供的那種可選參數與 rest 參數。你可使用 Common Lisp 化的參數列表,在使用 defun*
宏代替 defun
時,可支持關鍵字參數(keyword arguments,見後文的 defstruct
一節)。defun*
宏也容許使用 (return "foo")
這種控制流轉移方式來代替 catch/throw
機制。
若是你像讓本身定義的函數可以做爲 M-x
命令來執行,只需將 (interactive)
做爲函數體內的第一個表達式,亦即位於文檔化註釋字串以後。
在函數中要聲明局部變量,可以使用 let
表達式。基本語法是 (let var-decl var-decl)
:
(let ((name1 value1) (name2 value2) name3 name4 (name5 value5) name6 ...))
每一個 var-decl
要麼僅僅是變量名,要麼就是 (變量名 初始值)
形式。初始化的變量與未初始化的變量出現的次序是任意的。未初始化的變量,其值爲 nil
。
在一個函數中能夠有多條 let
表達式,可是爲了性能起見,一般是將變量聲明都放到開始的 let
表達式中,這樣會快一點。不過,你應該寫清晰的代碼。
C++ 有引用參數,函數能夠修改調用者堆棧中的變量。Java 沒有這個功能,所以有時你不得不迂迴的向函數傳遞單元素數組,或一個對象,或別的什麼東西來模擬這個功能。
Emacs Lisp 也沒有真正的向函數傳遞引用的機制,可是它有動態域(Dynamic Scope),這意味着你能夠用任何方式修改位於調用者堆棧中的變量。看下面這兩個函數:
(defun foo () (let ((x 6)) ; 定義了一個(棧中的)局部變量 x,將其初始化爲 6 (bar) ; 調用 bar 函數 x)) ; 返回 x (defun bar () (setq x 7)) ; 在調用者的棧中搜索 x 並修改它的值
若是你調用了 (foo)
,返回值爲 7。
動態域一般被認爲是近乎邪惡的壞設計,可是它有時也能派上用場。即便它真的很糟糕,經過它也能瞭解一些 Emacs 的內幕。
譯註:Emacs 24 對詞法域(Lexical Scope)提供了支持,可是 Emacs Lisp 默認依然是動態域。要開啓詞法域功能,可在 .el 文件的第一行添加如下信息:
;; -*- lexical-binding: t -*-
Lisp 函數默認是返回最後一個被求值的表達式的結果。經過一些構造技巧,也可讓每一個可能的返回結果安排在函數的尾部位置。例如:
上述 Lisp 函數 day-name
的返回值是最後一個表達式的求值結果,所以不管咱們怎麼嵌套 if
,都能自動產生一個結果返回,所以這裏不須要顯式的 return
語句。
不過,有時用 if
嵌套的方式來重構函數的返回形式會不太方便,它較適合一些小的函數。對於一些規模較大而且嵌套較深的函數,你可能但願函數可以在較早的時機返回。在 Emacs Lisp 中,這一需求可基於 break
與 continue
來實現。上文中的 day-name
可重構爲:
(defun day-name () (let ((date (calendar-day-of-week (calendar-current-date)))) ; 0-6 (catch 'return (case date (0 (throw 'return "Sunday")) (6 (throw 'return "Saturday")) (t (throw 'return "weekday"))))))
顯然,使用 catch/throw
會下降程序性能,可是有時你會須要用它來消除太深的嵌套結構。
前文已經講了 catch/throw
,它相似於異常,可用於控制流轉移。
Emacs 真正的錯誤處理機制叫作『條件』系統,本文不打算對此予以全面介紹,僅涉及如何捕捉異常以及如何忽略它們。
下面是一個通常化的 condition-case
結構,並且我也給出了 Java 的等價描述。
若是你想讓 cache
塊爲空,可以使用 ignore-errorse
:
(ignore-errors (do-something) (do-something-else))
有時你的啓動文件(譯註:多是 .emacs 或init.el文件)可能不是老是正確工做。可使用 ignore-errors
來封裝 Emacs Lisp 代碼,這樣即便被封裝的代碼出錯,也不會致使 Emacs 啓動失敗。
condition-case nil
的意思是『錯誤信息不賦給已命名的變量』。Emacs Lisp 容許你捕獲不一樣的錯誤類別並對錯誤信息進行排查。這方面的知識請從 Emacs Lisp 手冊獲取。
在 condition-case
塊內若是存在多條表達式須要求值,必須用 progn
將它們封裝起來。
condition-case
不會捕捉 throw
扔出來的值——這兩個系統是彼此獨立的。
Emacs Lisp 提供了相似 finally 的功能 unwind-protect
:
與 condition-case
類似,unwind-protect
接受單個體塊(body-form,譯註:try 部分),後面跟隨着一條或多條善後的表達式,所以你須要用 progn
將體塊內的表達式封裝起來。
若是讓 condition-case
(等價於 try/catch
)成爲 unwind-protect
(等價於 try/finally
)的體塊,那麼就能夠獲得 try/catch/finally
的效果:
(unwind-protect ; finally (condition-case nil ; try (progn ; { (do-something) ; body-1 (do-something-else)) ; body-2 } (error ; catch (message "oh no!") ; { catch 1 (poop-pants))) ; catch 2 } (first-finally-expr) ; { finally 1 (second-finally-expr)) ; finally 2 }
Emacs Lisp 不是標準意義上的面向對象編程語言,它沒有類、繼承以及多態等語法。Emacs 的 Common Lisp 包(如今的 cl-lib
)提供了一個有用的特性 defstruct
,經過它能夠實現簡單的 OOP 支持。下面我會給出一個簡單的示例。
下面的 Emacs Lisp 代碼與 Java 代碼本質上是等價的:
defstruct
宏提供了一個靈活的默認構造器,可是你也能夠根據本身的須要來定義相適的構造器。
defstruct
宏在建立對象實例時,也建立了一組斷定函數,它們的用法以下:
(person-p (make-person)) t (employee-p (make-person)) nil (employee-p (make-employee)) t (person-p (make-employee)) ; yes, it inherits from person t
Java 在對象構造器方面可能挺糟糕,不過 Emacs 在域(類成員)的設置方面挺糟糕。要設置類(結構體)的域,必須使用 setf
函數,而後將類名做爲域名的前綴:
這樣看上去,Lisp 並非太糟糕,可是在實踐中(由於 Emacs Lisp 不支持命名空間,而且也沒有 with-slots
宏),你會被捲入很長的類名與域名中的,例如:
(setf (js2-compiler-data-current-script-or-function compiler-data) current-script (js2-compiler-data-line-number compiler-data) current-line (js2-compiler-data-allow-member-expr-as-function-name compiler-data) allow (js2-compiler-data-language-version compiler-data) language-version)
要獲取域的值,須要將類名與域名鏈接起來,而後做爲函數來用:
(person-name steve) ; yields "Steve"
defstruct
還能作不少事——它的功能很是得體,該考慮的事都考慮了,儘管它沒能造成一個完善的面向對象系統。
在 Emacs Lisp 編程中,將緩衝區視爲類的實例每每頗有用。由於 Emacs 支持緩衝區級別的局部變量的概念——不管變量以那種方式設置(譯註,例如經過 setq
設置的變量),它們都會自動變成緩衝區內部的局部變量。所以,這些變量的行爲就像是被封裝在實例中的變量。
能夠用 make-variable-buffer-local
函數將一個變量聲明爲緩衝區級別的局部變量,一般這個函數會在 devar
或 defconst
以後出現(見下文)。
在 Emacs Lisp 中,能夠用 defvar
或 defconst
聲明變量,也能夠爲變量提供文檔化註釋:
(defconst pi 3.14159 "A gross approximation of pi.")
語法爲 (defvar 變量名 值 [文檔化註釋])
。
不過,會讓你大跌眼鏡的是,defconst
定義的是變量,而 defvar
定義的是常量,至少在從新求值時是這樣。要改變 defvar
變量的值,須要使用 makeunbound
來解除變量的綁定。不過,老是可使用 setq
來修改 defvar
或 defconst
變量的值。這兩種變量形式,僅有的區別是,defconst
能夠表達一種意圖:你定義的是一個常量。
可使用 setq
來建立全新的變量,可是若是用 defvar
,Emacs Lisp 的字節碼編譯器能捕捉到一些錯誤信息。
Emacs Lisp 是一種真正的編程語言。它有編譯器、調試器、性能分析器、效果顯示器、運行時文檔、庫、輸入/輸出、網絡、進程控制等。它有不少東西值得學習,可是我但願這篇小文章可以讓你向它邁出第一步。
不管 Emacs Lisp 有多麼古怪和煩人,只要你上手了,它就能讓你體驗到編程的快樂。做爲一種編程語言,它並不偉大,並且每一個人都指望它是 Common Lisp 或 Scheme 或其餘某種更好的 Lisp 方言。有些人甚至認爲它根本不是 Lisp。
可是,要定製你的 Emacs,或者修復你從他人那裏獲得的 Emacs Lisp 代碼,那麼 Emacs Lisp 就會很是很是有用。四兩 Emacs Lisp 可撥千鈞之物。
正在學習 Emacs Lisp 的你,若是以爲這份文檔是有用的,請告訴我。若是你打算寫一些 Emacs 擴展,能夠告訴我你但願個人下一篇文檔要寫什麼。有興趣的化,我會再繼續這個 Emergency Elisp 系列。
Good Luck!
譯註:做者彷佛沒有再寫下去。xahlee 在 http://ergoemacs.org/emacs/elisp.html 所寫的系列文檔可做爲進階教程。