源代碼下載:?learn-emacs-lisp-zh.el
;; 15分鐘學(xué)會Emacs Lisp (v0.2a)
;;(作者:bzg,https://github.com/bzg
;; 譯者:lichenbo,http://douban.com/people/lichenbo)
;;
;; 請先閱讀Peter Norvig的一篇好文:
;; http://norvig.com/21-days.html
;; (譯者注:中文版請見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
;; 很重要的警告:
;;
;; 按照這個教程來學(xué)習(xí)并不會對你的電腦有任何損壞
;; 除非你自己在學(xué)習(xí)的過程中憤怒地把它砸了
;; 如果出現(xiàn)了這種情況,我不會承擔(dān)任何責(zé)任
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 打開emacs
;;
;; 按'q'消除歡迎界面
;;
;; 現(xiàn)在請注意窗口底部的那一個灰色長條
;;
;; "*scratch*" 是你現(xiàn)在編輯界面的名字。
;; 這個編輯界面叫做一個"buffer"。
;;
;; 每當(dāng)你打開Emacs時,都會默認(rèn)打開這個scratch buffer
;; 此時你并沒有在編輯任何文件,而是在編輯一個buffer
;; 之后你可以將這個buffer保存到一個文件中。
;;
;; 之后的"Lisp interaction" 則是表明我們可以用的某組命令
;;
;; Emacs在每個buffer中都有一組內(nèi)置的命令
;; 而當(dāng)你激活某種特定的模式時,就可以使用相應(yīng)的命令
;; 這里我們使用`lisp-interaction-mode',
;; 這樣我們就可以使用內(nèi)置的Emacs Lisp(以下簡稱Elisp)命令了。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 分號是注釋開始的標(biāo)志
;;
;; Elisp 是由符號表達式構(gòu)成的 (即"s-表達式"或"s式"):
(+ 2 2)
;; 這個s式的意思是 "對2進行加2操作".
;; s式周圍有括號,而且也可以嵌套:
(+ 2 (+ 1 1))
;; 一個s式可以包含原子符號或者其他s式
;; 在上面的例子中,1和2是原子符號
;; (+ 2 (+ 1 1)) 和 (+ 1 1) 是s式.
;; 在 `lisp-interaction-mode' 中你可以計算s式.
;; 把光標(biāo)移到閉括號后,之后按下ctrl+j(以后簡寫為'C-j')
(+ 3 (+ 1 2))
;; ^ 光標(biāo)放到這里
;; 按下`C-j' 就會輸出 6
;; `C-j' 會在buffer中插入當(dāng)前運算的結(jié)果
;; 而`C-xC-e' 則會在emacs最底部顯示結(jié)果,也就是被稱作"minibuffer"的區(qū)域
;; 為了避免把我們的buffer填滿無用的結(jié)果,我們以后會一直用`C-xC-e'
;; `setq' 可以將一個值賦給一個變量
(setq my-name "Bastien")
;; `C-xC-e' 輸出 "Bastien" (在 mini-buffer 中顯示)
;; `insert' 會在光標(biāo)處插入字符串:
(insert "Hello!")
;; `C-xC-e' 輸出 "Hello!"
;; 在這里我們只傳給了insert一個參數(shù)"Hello!", 但是
;; 我們也可以傳給它更多的參數(shù),比如2個:
(insert "Hello" " world!")
;; `C-xC-e' 輸出 "Hello world!"
;; 你也可以用變量名來代替字符串
(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í)行這個函數(shù)
(hello)
;; `C-xC-e' 輸出 Hello, I am Bastien
;; 函數(shù)中空括號的意思是我們不需要接受任何參數(shù)
;; 但是我們不能一直總是用my-name這個變量
;; 所以我們現(xiàn)在使我們的函數(shù)接受一個叫做"name"的參數(shù)
(defun hello (name) (insert "Hello " name))
;; `C-xC-e' 輸出 hello
;; 現(xiàn)在我們調(diào)用這個函數(shù),并且將"you"作為參數(shù)傳遞
(hello "you")
;; `C-xC-e' 輸出 "Hello you"
;; 成功!
;; 現(xiàn)在我們可以休息一下
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 下面我們在新的窗口中新建一個名為 "*test*" 的buffer:
(switch-to-buffer-other-window "*test*")
;; `C-xC-e' 這時屏幕上會顯示兩個窗口,而光標(biāo)此時位于*test* buffer內(nèi)
;; 用鼠標(biāo)單擊上面的buffer就會使光標(biāo)移回。
;; 或者你可以使用 `C-xo' 使得光標(biāo)跳到另一個窗口中
;; 你可以用 `progn'命令將s式結(jié)合起來:
(progn
(switch-to-buffer-other-window "*test*")
(hello "you"))
;; `C-xC-e' 此時屏幕分為兩個窗口,并且在*test* buffer中顯示"Hello you"
;; 現(xiàn)在為了簡潔,我們需要在每個s式后面都使用`C-xC-e'來執(zhí)行,后面就不再說明了
;; 記得可以用過鼠標(biāo)或者`C-xo'回到*scratch*這個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' 將一個值和一個局部變量綁定:
(let ((local-name "you"))
(switch-to-buffer-other-window "*test*")
(erase-buffer)
(hello local-name)
(other-window 1))
;; 這里我們就不需要使用 `progn' 了, 因為 `let' 也可以將很多s式組合起來。
;; 格式化字符串的方法:
(format "Hello %s!\n" "visitor")
;; %s 是字符串占位符,這里被"visitor"替代.
;; \n 是換行符。
;; 現(xiàn)在我們用格式化的方法再重寫一下我們的函數(shù):
(defun hello (name)
(insert (format "Hello %s!\n" name)))
(hello "you")
;; 我們再用`let'新建另一個函數(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: ")
;; 這個函數(shù)會返回在執(zhí)行時用戶輸入的信息
;; 現(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é)果在另一個窗口中顯示:
(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)))
;; 測試一下:
(greeting "Bastien")
;; 第二節(jié)結(jié)束,休息一下吧。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 我們將一些名字存到列表中:
(setq list-of-names '("Sarah" "Chloe" "Mathilde"))
;; 用 `car'來取得第一個名字:
(car list-of-names)
;; 用 `cdr'取得剩下的名字:
(cdr list-of-names)
;; 用 `push'把名字添加到列表的開頭:
(push "Stephanie" list-of-names)
;; 注意: `car' 和 `cdr' 并不修改列表本身, 但是 `push' 卻會對列表本身進行操作.
;; 這個區(qū)別是很重要的: 有些函數(shù)沒有任何副作用(比如`car')
;; 但還有一些卻是有的 (比如 `push').
;; 我們來對`list-of-names'列表中的每一個元素都使用hello函數(shù):
(mapcar 'hello list-of-names)
;; 將 `greeting' 改進,使的我們能夠?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ù)嗎? 這個函數(shù)接受一個參數(shù),名字。
;; `mapcar' 調(diào)用 `hello', 并將`list-of-names'作為參數(shù)先后傳給`hello'
;; 現(xiàn)在我們對顯示的buffer中的內(nèi)容進行一些更改:
(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返回某個值時執(zhí)行y這個s式
;; 當(dāng)x返回`nil' (空), 退出循環(huán)
(replace-hello-by-bonjour)
;; 你會看到所有在*test* buffer中出現(xiàn)的"Hello"字樣都被換成了"Bonjour"
;; 你也會得到以下錯誤提示: "Search failed: Hello".
;;
;; 如果要避免這個錯誤, 你需要告訴 `search-forward' 這個命令是否在
;; buffer的某個地方停止查找, 并且在什么都沒找到時是否應(yīng)該不給出錯誤提示
;; (search-forward "Hello" nil t) 可以達到這個要求:
;; `nil' 參數(shù)的意思是 : 查找并不限于某個范圍內(nèi)
;; `t' 參數(shù)的意思是: 當(dāng)什么都沒找到時,不給出錯誤提示
;; 在下面的函數(shù)中,我們用到了s式,并且不給出任何錯誤提示:
(defun hello-to-bonjour ()
(switch-to-buffer-other-window "*test*")
(erase-buffer)
;; 為`list-of-names'中的每個名字調(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)
;; 給這些名字上個色:
(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))
;; 這個函數(shù)使用了 `re-search-forward':
;; 和查找一個字符串不同,你用這個命令可以查找一個模式,即正則表達式
;; 正則表達式 "Bonjour \\(.+\\)!" 的意思是:
;; 字符串 "Bonjour ", 之后跟著
;; 一組 | \\( ... \\) 結(jié)構(gòu)
;; 任意字符 | . 的含義
;; 有可能重復(fù)的 | + 的含義
;; 之后跟著 "!" 這個字符串
;; 準(zhǔn)備好了?試試看。
(boldify-names)
;; `add-text-properties' 可以添加文字屬性, 比如文字樣式
;; 好的,我們成功了!
;; 如果你想對一個變量或者函數(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
更多建議: