【感謝的@iamxuxiao 熱心翻譯。如果其他朋友也有不錯的原創或譯文,可以嘗試推薦給伯樂線上。】
Emacs Lisp,一種直譯式的指令碼語言,為LISP的方言之一,GNU Emacs與XEmacs文字編輯器都使用這個程式語言來擴充套件它們的功能。它的直譯器是以C語言來實作的。它受到Maclisp的影響很大,但是跟Common Lisp與Scheme有所不同。(摘自維基百科)
本文作者建議大家在看『 XXX 10 分鐘入門』一類文章之前,先閱讀 Peter Norvig 的《Teach Yourself Programming in Ten Years | 自學程式設計,十年磨一劍》。
Peter Norvig 指出學習程式語言沒有捷徑,真正掌握任何一門語言,都至少需要10年的時間,並且不斷的練習實踐。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
;; This gives an introduction to Emacs Lisp in 15 minutes (v0.2d) ;; ;; 英文原作者: Bastien / @bzg2 / http://bzg.fr ;; 中文翻譯: iamxuxiao ;; ;; ;; 如何安裝 Emacs ;; ;; Debian: apt-get install emacs (or see your distro instructions) ;; 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 ;; ;; More general information can be found at: ;; http://www.gnu.org/software/emacs/#Obtaining ;; 免責宣告: ;; ;; Going through this tutorial won't damage your computer unless ;; you get so angry that you throw it on the floor. In that case, ;; I hereby decline any responsability. Have fun! == 啟動Emacs, 緩衝區和工作模式== ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; 第一步首先啟動Emacs: (在windows中可以雙擊emacs圖示,在Linux中可以輸入% emacs & ), ;; 然後在鍵盤上鍵入q 跳過系統歡迎的資訊, ;; 先觀察在Emacs螢幕的底部,會給出一堆關於當前的工作情況的資訊,其中灰色的一行叫做狀態行, ;; 在其中你會發現 *scratch* 的字樣,這表示你當前的緩衝區(buffer)的名字。 ;; 緩衝區也叫做工作區,在Emacs中開啟一個檔案,實際只是在Emacs中構造該檔案的一個副本,放到緩衝區中, ;; 在Emacs中對該檔案的編輯也是針對該副本的編輯,唯有儲存改動時,Emacs才會把緩衝區中的內容在複製到原檔案中去。 ;; 狀態行下面的那行,叫做輔助輸入區(minibuffer),該minibuffer用於顯示計算結果,以及和使用者做互動。 ;; ;; ;; 如何切換Emacs的工作模式 ;; Emacs有各種各樣功能各異的模式,工作模式的含義其實就是Emacs對當前的文字編輯工作 ;; 更加的敏感,比如高亮和縮排,並且支援一些特殊的命令。 ;; 為了實驗本教程中的lisp命令,我們要讓Emacs工作在lisp-interaction-mode工作模式下, ;; 這個模式可以讓我們在緩衝區中和Emacs進行互動,並且直接執行Lisp命令,得到結果。 ;; 進入lisp-interaction-mode的方法: 把游標移動到輔助輸入區,鍵入M-x lisp-interaction-mode ;; 然後回車。 == 表示式,變數和函式 == ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; 冒號在Lisp中表示註釋 ;; 在Elisp中做運算,呼叫函式的最簡單的方式是 ;; (function arg1 arg2) ;; 這相當於通常的function(arg1,arg2),下面的表示式,對兩個數字進行加法運算 (+ 2 2) ;; Elisp中表示式可以通過括號來巢狀 (+ 2 (+ 1 1)) ;; 在lisp-interaction-mode模式中,我們可以直接計算一個表示式,計算的方法是 (+ 3 (+ 1 2)) ;; ^ 把游標放在這裡,並且鍵入Ctrl-j (之後將簡寫成C-j) ;; C-j是一個快捷命令,在後臺,該快捷鍵將呼叫求值命令,並且把計算的結果 ;; 插入到當前的緩衝區中 ;; 如果不希望Emacs在緩衝區中插入計算結果,我們還可以在表示式的末尾使用C-x C-e組合鍵 ;; C-x C-e的意思是: 先按下Ctrl-x 再按下Ctrl-e ;; 這個命令會讓Emacs在輔助緩衝區,也就是Emacs視窗的最底部那行顯示計算結果 ;; ELisp中的賦值函式是是setq,下面的表示式給變數my-name賦值"Bastien" (setq my-name "Bastien") ;; ^ 把游標停在這裡,再鍵入C-x C-e ;; 下面insert函式的作用是在游標所在出插入字元Hello (insert "Hello!") ;; ^ 把游標停在這裡,再鍵入C-x C-e ;; insert函式還可以兩個常量字元,比如 (insert "Hello" " world!") ;; insert函式還可以接受變數作為引數,我們之前已經給my-name變數賦過值了 ;; 所以下面命令的輸出結果是 "Hello, I am Bastien" (insert "Hello, I am " my-name) ;; defun命令用來定義一個函式,語法是 ;; (defun 函式名 (引數列表) (函式體)) (defun hello () (insert "Hello, I am " my-name)) ;; ^ 把游標停在這裡,再鍵入C-x C-e 執行defun命令來定義函式 ;; 通過defun命令,你已經在Emacs中安裝了這個hello函式,這個函式就成為了Emacs的一部分,知道你退出Emacs或者改變hello的定義 ;; 從下面開始,我們將不再提醒讀者使用C-x C-e來定義函式和執行ELisp指令 ;; 在Elisp中直接輸入函式的名稱就是呼叫該函式。 ;; 下面的命令的輸入結果是: Hello, I am Bastien (hello) ;; 前面定義的hello函式不接受任何引數,過於簡單, ;; 現在我們重新定義hello函式,讓它接受一個引數name。 (defun hello (name) (insert "Hello " name)) ;; 然後呼叫新的hello函式,並且提供一個引數。 ;; 下面命令的輸出結果是"Hello you" (hello "you") == progn,let和互動式函式== ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; 執行switch-to-buffer-other-window命令,將在在一個新的視窗中開啟一個buffer ;; 該buffer命名叫做 test, 並且把游標移到新的buffer的視窗中。 (switch-to-buffer-other-window "*test*") ;; 要回到原來的buffer中,可以使用滑鼠點選原來的buffer ;; 或者使用組合鍵 C-x o ;; C-x o的意思是: 先按下Ctrl-x 再按下o ;; 如果要執行一系列的指令,可以使用流程函式progn,把函式命令連線起來. ;; 下面的命令,先開啟一個新的buffer,再執行hello函式,該hello函式的引數是"you" (progn (switch-to-buffer-other-window "*test*") (hello "you")) ;; 如果要清空一個buffer,可以呼叫erase-buffer函式。下面的命令先清空test buffer,再呼叫hello函式做列印 (progn (switch-to-buffer-other-window "*test*") (erase-buffer) (hello "there")) ;; 在這一系列的質量後面再新增呼叫一個other-window函式,這樣在hello函式被呼叫完畢之後 ;; 游標自動回到之前的buffer視窗中 (progn (switch-to-buffer-other-window "*test*") (erase-buffer) (hello "you") (other-window 1)) ;; let函式用來做區域性變數的定義 下面的一系列命令中 ;; let函式首先定義local-name變數的值為“you” ;; 然後接著執行括號中其它的語句塊部分,這個功能和progn類似 (let ((local-name "you")) (switch-to-buffer-other-window "*test*") (erase-buffer) (hello local-name) (other-window 1)) ;; format函式可以用做格式化的輸出 其中%s表示該s的地方將被之後提供的一個字串,即visitor替換 ;; \n表示換行 (format "Hello %s!\n" "visitor") ;; 現在我們利用format函式來改進之前定義的hello函式 (defun hello (name) (insert (format "Hello %s!\n" name))) ;; 執行這個函式結果是"Hello you",並且游標換到下一行 (hello "you") ;; 下面我們再設計一個greeting函式,該函式接受一個引數name, ;; 在函式體的內部又使用了let函式,給一個區域性變數your-name賦值 ;; 最後把引數和區域性變數格式化的列印出來 (defun greeting (name) (let ((your-name "Bastien")) (insert (format "Hello %s!\n\nI am %s." name your-name ; 區域性變數 )))) ;; 執行greeting函式,並提供"you"字串作為引數 (greeting "you") ;; read-from-minibuffer函式提供和使用者互動的功能,這個函式可以幫助Elisp程式從使用者處得到輸入 (read-from-minibuffer "Enter your name: ") ;; 比如如果我們希望greeting函式能夠從使用者處得到姓名,並且做列印格式化的歡迎資訊。 ;; 可以先呼叫read-from-minibuffer在minibuffer中提示使用者輸入姓名, ;; 然後把得到的結果賦給區域性變數your-name, ;; 最後insert函式在當前buffer中插入格式化的輸出 (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 ; 格式化輸出引數1 your-name ; 格式化輸出引數2 )))) ;; 執行這個函式 (greeting "Bastien") ;; 再稍加改進greeting 把結果列印在新的buffer中 (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") == 列表和綜合例項 == ;; Lisp中使用括號構造列表,使用setq給變數賦值。 ;; 下面的命令先構造一個列表,再把這個列表賦給list-of-names變數 (setq list-of-names '("Sarah" "Chloe" "Mathilde")) ;; ^這裡的單引號表示這是一個列表 ;; 如果想要得到列表中的第一個元素,可以使用car函式 (car list-of-names) ;; 如果想要得到列表中的除第一個元素以外的其它元素,可以使用cdr函式 (cdr list-of-names) ;; 以後push函式可以在列表的頭部插入新的元素,所以下面的命令將改變list-of-name中元素的個數 (push "Stephanie" list-of-names) ;; mapcar函式對列表中的把列表中的每一個元素分別取出來,賦給hello函式 (mapcar 'hello list-of-names) ;; 重新定義greeting函式,在一個新的,清空的buffer中,對list-of-names列表中的每一個元素,呼叫hello函式 ;; 呼叫完畢之後,再讓游標回到原的buffer中 (defun greeting () (switch-to-buffer-other-window "*test*") (erase-buffer) (mapcar 'hello list-of-names) (other-window 1)) ;;執行這個函式,我們將得到一個名叫test的buffer,其中的內容是 ;; Hello Stephanie! ;; Hello Sarah! ;; Hello Chloe! ;; Hello Mathilde! ;; 暫時先不要關閉這個buffer!後面還有用! (greeting) ;; 下面我們對buffer做一些更有意思的事情! ;; 定義一個replace-hello-by-bonjour函式,顧名思義,就是把hello替換成bonjour ;; 該函式首先把游標移到一個叫做test的buffer中 ;; 再把游標移到該buffer的開頭 ;; 從頭開始搜尋字串Hello,並且替換成Bonjour ;; 結束之後在把游標移會到一開始的buffer中。 (defun replace-hello-by-bonjour () (switch-to-buffer-other-window "*test*") (goto-char (point-min)) ;該函式把游標移到buffer的開頭 (while (search-forward "Hello") (replace-match "Bonjour")) (other-window 1)) ;; 其中 (search-forward "Hello") 在當前的buffer中做前向搜尋 ;; (while x y) 當x 的條件滿足時執行y指令 ,當x返回nil時,while迴圈結束 ;; 執行這個函式 替換test buffer中的hello (replace-hello-by-bonjour) ;; test buffer中的結果如下 ;; Bonjour Stephanie! ;; Bonjour Sarah! ;; Bonjour Chloe! ;; Bonjour Mathilde! ;; 在minibuff中,還會有一條錯誤資訊 "Search failed: Hello". ;; 把(search-forward "Hello")一句換成如下就不會有錯誤資訊了 ;; (search-forward "Hello" nil t) ;; 其中 nil參數列示 搜尋的區域不加限制,直到buffer結束 ;; 其中t引數指示search-foward函式 跳過錯誤資訊 直接退出 ;; 新hello-to-bonjour如下: (defun hello-to-bonjour () (switch-to-buffer-other-window "*test*") (erase-buffer) ;; 對list-of-names列表中的每個元素 使用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) ;; 下面的boldify-names 函式 , ;; 首先把游標挪到名叫test的buffer的開頭, ;; 然後使用regular expression 搜尋 “Bonjour + 其它任何內容” 的pattern, ;; 然後對找到的字元加粗。 (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)) ;; 執行這個函式 (boldify-names) == 幫助和參考== ;; 在Emacs中我們可以通過如下的方式得到變數和函式的幫助資訊 ;; C-h v a-variable RET ;; C-h f a-function RET ;; ;; 下面的命令將開啟整個Emacs Manual ;; ;; C-h i m elisp RET ;; ;; Emacs Lisp 教程 ;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html ;; Thanks to these people for their feedback and suggestions: ;; - Wes Hardaker ;; - notbob ;; - Kevin Montuori ;; - Arne Babenhauserheide ;; - Alan Schmitt ;; - LinXitoW ;; - Aaron Meurer |