源代碼下載:?learn-emacs-lisp-zh.el
;; 15分鐘學(xué)會(huì)Emacs Lisp (v0.2a)
;;(作者:bzg,https://github.com/bzg
;; 譯者:lichenbo,http://douban.com/people/lichenbo)
;;
;; 請(qǐng)先閱讀Peter Norvig的一篇好文:
;; http://norvig.com/21-days.html
;; (譯者注:中文版請(qǐng)見http://blog.youxu.info/21-days/)
;;
;; 之后安裝GNU Emacs 24.3:
;;
;; Debian: apt-get install emacs (視具體發(fā)行版而定)
;; MacOSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg
;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip
;;
;; 更多信息可以在這里找到:
;; http://www.gnu.org/software/emacs/#Obtaining
;; 很重要的警告:
;;
;; 按照這個(gè)教程來(lái)學(xué)習(xí)并不會(huì)對(duì)你的電腦有任何損壞
;; 除非你自己在學(xué)習(xí)的過(guò)程中憤怒地把它砸了
;; 如果出現(xiàn)了這種情況,我不會(huì)承擔(dān)任何責(zé)任
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 打開emacs
;;
;; 按'q'消除歡迎界面
;;
;; 現(xiàn)在請(qǐng)注意窗口底部的那一個(gè)灰色長(zhǎng)條
;;
;; "*scratch*" 是你現(xiàn)在編輯界面的名字。
;; 這個(gè)編輯界面叫做一個(gè)"buffer"。
;;
;; 每當(dāng)你打開Emacs時(shí),都會(huì)默認(rèn)打開這個(gè)scratch buffer
;; 此時(shí)你并沒有在編輯任何文件,而是在編輯一個(gè)buffer
;; 之后你可以將這個(gè)buffer保存到一個(gè)文件中。
;;
;; 之后的"Lisp interaction" 則是表明我們可以用的某組命令
;;
;; Emacs在每個(gè)buffer中都有一組內(nèi)置的命令
;; 而當(dāng)你激活某種特定的模式時(shí),就可以使用相應(yīng)的命令
;; 這里我們使用`lisp-interaction-mode',
;; 這樣我們就可以使用內(nèi)置的Emacs Lisp(以下簡(jiǎn)稱Elisp)命令了。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 分號(hào)是注釋開始的標(biāo)志
;;
;; Elisp 是由符號(hào)表達(dá)式構(gòu)成的 (即"s-表達(dá)式"或"s式"):
(+ 2 2)
;; 這個(gè)s式的意思是 "對(duì)2進(jìn)行加2操作".
;; s式周圍有括號(hào),而且也可以嵌套:
(+ 2 (+ 1 1))
;; 一個(gè)s式可以包含原子符號(hào)或者其他s式
;; 在上面的例子中,1和2是原子符號(hào)
;; (+ 2 (+ 1 1)) 和 (+ 1 1) 是s式.
;; 在 `lisp-interaction-mode' 中你可以計(jì)算s式.
;; 把光標(biāo)移到閉括號(hào)后,之后按下ctrl+j(以后簡(jiǎn)寫為'C-j')
(+ 3 (+ 1 2))
;; ^ 光標(biāo)放到這里
;; 按下`C-j' 就會(huì)輸出 6
;; `C-j' 會(huì)在buffer中插入當(dāng)前運(yùn)算的結(jié)果
;; 而`C-xC-e' 則會(huì)在emacs最底部顯示結(jié)果,也就是被稱作"minibuffer"的區(qū)域
;; 為了避免把我們的buffer填滿無(wú)用的結(jié)果,我們以后會(huì)一直用`C-xC-e'
;; `setq' 可以將一個(gè)值賦給一個(gè)變量
(setq my-name "Bastien")
;; `C-xC-e' 輸出 "Bastien" (在 mini-buffer 中顯示)
;; `insert' 會(huì)在光標(biāo)處插入字符串:
(insert "Hello!")
;; `C-xC-e' 輸出 "Hello!"
;; 在這里我們只傳給了insert一個(gè)參數(shù)"Hello!", 但是
;; 我們也可以傳給它更多的參數(shù),比如2個(gè):
(insert "Hello" " world!")
;; `C-xC-e' 輸出 "Hello world!"
;; 你也可以用變量名來(lái)代替字符串
(insert "Hello, I am " my-name)
;; `C-xC-e' 輸出 "Hello, I am Bastien"
;; 你可以把s式嵌入函數(shù)中
(defun hello () (insert "Hello, I am " my-name))
;; `C-xC-e' 輸出 hello
;; 現(xiàn)在執(zhí)行這個(gè)函數(shù)
(hello)
;; `C-xC-e' 輸出 Hello, I am Bastien
;; 函數(shù)中空括號(hào)的意思是我們不需要接受任何參數(shù)
;; 但是我們不能一直總是用my-name這個(gè)變量
;; 所以我們現(xiàn)在使我們的函數(shù)接受一個(gè)叫做"name"的參數(shù)
(defun hello (name) (insert "Hello " name))
;; `C-xC-e' 輸出 hello
;; 現(xiàn)在我們調(diào)用這個(gè)函數(shù),并且將"you"作為參數(shù)傳遞
(hello "you")
;; `C-xC-e' 輸出 "Hello you"
;; 成功!
;; 現(xiàn)在我們可以休息一下
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 下面我們?cè)谛碌拇翱谥行陆ㄒ粋€(gè)名為 "*test*" 的buffer:
(switch-to-buffer-other-window "*test*")
;; `C-xC-e' 這時(shí)屏幕上會(huì)顯示兩個(gè)窗口,而光標(biāo)此時(shí)位于*test* buffer內(nèi)
;; 用鼠標(biāo)單擊上面的buffer就會(huì)使光標(biāo)移回。
;; 或者你可以使用 `C-xo' 使得光標(biāo)跳到另一個(gè)窗口中
;; 你可以用 `progn'命令將s式結(jié)合起來(lái):
(progn
(switch-to-buffer-other-window "*test*")
(hello "you"))
;; `C-xC-e' 此時(shí)屏幕分為兩個(gè)窗口,并且在*test* buffer中顯示"Hello you"
;; 現(xiàn)在為了簡(jiǎn)潔,我們需要在每個(gè)s式后面都使用`C-xC-e'來(lái)執(zhí)行,后面就不再說(shuō)明了
;; 記得可以用過(guò)鼠標(biāo)或者`C-xo'回到*scratch*這個(gè)buffer。
;; 清除當(dāng)前buffer也是常用操作之一:
(progn
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello "there"))
;; 也可以回到其他的窗口中
(progn
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello "you")
(other-window 1))
;; 你可以用 `let' 將一個(gè)值和一個(gè)局部變量綁定:
(let ((local-name "you"))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello local-name)
(other-window 1))
;; 這里我們就不需要使用 `progn' 了, 因?yàn)?`let' 也可以將很多s式組合起來(lái)。
;; 格式化字符串的方法:
(format "Hello %s!\n" "visitor")
;; %s 是字符串占位符,這里被"visitor"替代.
;; \n 是換行符。
;; 現(xiàn)在我們用格式化的方法再重寫一下我們的函數(shù):
(defun hello (name)
(insert (format "Hello %s!\n" name)))
(hello "you")
;; 我們?cè)儆胉let'新建另一個(gè)函數(shù):
(defun greeting (name)
(let ((your-name "Bastien"))
(insert (format "Hello %s!\n\nI am %s."
name ; the argument of the function
your-name ; the let-bound variable "Bastien"
))))
;; 之后執(zhí)行:
(greeting "you")
;; 有些函數(shù)可以和用戶交互:
(read-from-minibuffer "Enter your name: ")
;; 這個(gè)函數(shù)會(huì)返回在執(zhí)行時(shí)用戶輸入的信息
;; 現(xiàn)在我們讓`greeting'函數(shù)顯示你的名字:
(defun greeting (from-name)
(let ((your-name (read-from-minibuffer "Enter your name: ")))
(insert (format "Hello!\n\nI am %s and you are %s."
from-name ; the argument of the function
your-name ; the let-bound var, entered at prompt
))))
(greeting "Bastien")
;; 我們讓結(jié)果在另一個(gè)窗口中顯示:
(defun greeting (from-name)
(let ((your-name (read-from-minibuffer "Enter your name: ")))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(insert (format "Hello %s!\n\nI am %s." your-name from-name))
(other-window 1)))
;; 測(cè)試一下:
(greeting "Bastien")
;; 第二節(jié)結(jié)束,休息一下吧。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 我們將一些名字存到列表中:
(setq list-of-names '("Sarah" "Chloe" "Mathilde"))
;; 用 `car'來(lái)取得第一個(gè)名字:
(car list-of-names)
;; 用 `cdr'取得剩下的名字:
(cdr list-of-names)
;; 用 `push'把名字添加到列表的開頭:
(push "Stephanie" list-of-names)
;; 注意: `car' 和 `cdr' 并不修改列表本身, 但是 `push' 卻會(huì)對(duì)列表本身進(jìn)行操作.
;; 這個(gè)區(qū)別是很重要的: 有些函數(shù)沒有任何副作用(比如`car')
;; 但還有一些卻是有的 (比如 `push').
;; 我們來(lái)對(duì)`list-of-names'列表中的每一個(gè)元素都使用hello函數(shù):
(mapcar 'hello list-of-names)
;; 將 `greeting' 改進(jìn),使的我們能夠?qū)list-of-names'中的所有名字執(zhí)行:
(defun greeting ()
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(mapcar 'hello list-of-names)
(other-window 1))
(greeting)
;; 記得我們之前定義的 `hello' 函數(shù)嗎? 這個(gè)函數(shù)接受一個(gè)參數(shù),名字。
;; `mapcar' 調(diào)用 `hello', 并將`list-of-names'作為參數(shù)先后傳給`hello'
;; 現(xiàn)在我們對(duì)顯示的buffer中的內(nèi)容進(jìn)行一些更改:
(defun replace-hello-by-bonjour ()
(switch-to-buffer-other-window "*test*")
(goto-char (point-min))
(while (search-forward "Hello")
(replace-match "Bonjour"))
(other-window 1))
;; (goto-char (point-min)) 將光標(biāo)移到buffer的開始
;; (search-forward "Hello") 查找字符串"Hello"
;; (while x y) 當(dāng)x返回某個(gè)值時(shí)執(zhí)行y這個(gè)s式
;; 當(dāng)x返回`nil' (空), 退出循環(huán)
(replace-hello-by-bonjour)
;; 你會(huì)看到所有在*test* buffer中出現(xiàn)的"Hello"字樣都被換成了"Bonjour"
;; 你也會(huì)得到以下錯(cuò)誤提示: "Search failed: Hello".
;;
;; 如果要避免這個(gè)錯(cuò)誤, 你需要告訴 `search-forward' 這個(gè)命令是否在
;; buffer的某個(gè)地方停止查找, 并且在什么都沒找到時(shí)是否應(yīng)該不給出錯(cuò)誤提示
;; (search-forward "Hello" nil t) 可以達(dá)到這個(gè)要求:
;; `nil' 參數(shù)的意思是 : 查找并不限于某個(gè)范圍內(nèi)
;; `t' 參數(shù)的意思是: 當(dāng)什么都沒找到時(shí),不給出錯(cuò)誤提示
;; 在下面的函數(shù)中,我們用到了s式,并且不給出任何錯(cuò)誤提示:
(defun hello-to-bonjour ()
(switch-to-buffer-other-window "*test*")
(erase-buffer)
;; 為`list-of-names'中的每個(gè)名字調(diào)用hello
(mapcar 'hello list-of-names)
(goto-char (point-min))
;; 將"Hello" 替換為"Bonjour"
(while (search-forward "Hello" nil t)
(replace-match "Bonjour"))
(other-window 1))
(hello-to-bonjour)
;; 給這些名字上個(gè)色:
(defun boldify-names ()
(switch-to-buffer-other-window "*test*")
(goto-char (point-min))
(while (re-search-forward "Bonjour \\(.+\\)!" nil t)
(add-text-properties (match-beginning 1)
(match-end 1)
(list 'face 'bold)))
(other-window 1))
;; 這個(gè)函數(shù)使用了 `re-search-forward':
;; 和查找一個(gè)字符串不同,你用這個(gè)命令可以查找一個(gè)模式,即正則表達(dá)式
;; 正則表達(dá)式 "Bonjour \\(.+\\)!" 的意思是:
;; 字符串 "Bonjour ", 之后跟著
;; 一組 | \\( ... \\) 結(jié)構(gòu)
;; 任意字符 | . 的含義
;; 有可能重復(fù)的 | + 的含義
;; 之后跟著 "!" 這個(gè)字符串
;; 準(zhǔn)備好了?試試看。
(boldify-names)
;; `add-text-properties' 可以添加文字屬性, 比如文字樣式
;; 好的,我們成功了!
;; 如果你想對(duì)一個(gè)變量或者函數(shù)有更多的了解:
;;
;; C-h v 變量 回車
;; C-h f 函數(shù) 回車
;;
;; 閱讀Emacs Lisp官方文檔:
;;
;; C-h i m elisp 回車
;;
;; 在線閱讀Emacs Lisp文檔:
;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html
;; 感謝以下同學(xué)的建議和反饋:
;; - Wes Hardaker
;; - notbob
;; - Kevin Montuori
;; - Arne Babenhauserheide
;; - Alan Schmitt
更多建議: