Y分鐘學Elisp

weakish發表於2014-04-28

請先閱讀Peter Norvig的一篇好文: http://norvig.com/21-days.html
(譯者注:中文版請見http://blog.youxu.info/21-days/

之後安裝GNU Emacs 24.3:

更多資訊可以在這裡找到:

http://www.gnu.org/software/emacs/#Obtaining

很重要的警告:

  • 按照這個教程來學習並不會對你的電腦有任何損壞
  • 除非你自己在學習的過程中憤怒地把它砸了
  • 如果出現了這種情況,我不會承擔任何責任

開啟emacs

按`q`消除歡迎介面

現在請注意視窗底部的那一個灰色長條

scratch” 是你現在編輯介面的名字。這個編輯介面叫做一個”buffer”。 每當你開啟Emacs時,都會預設開啟這個scratch buffer。 此時你並沒有在編輯任何檔案,而是在編輯一個buffer。 之後你可以將這個buffer儲存到一個檔案中。

之後的”Lisp interaction” 則是表明我們可以用的某組命令。Emacs在每個buffer中都有一組內建的命令。 而當你啟用某種特定的模式時,就可以使用相應的命令。 這裡我們使用lisp-interaction-mode, 這樣我們就可以使用內建的Emacs Lisp(以下簡稱Elisp)命令了。

;; 分號是註釋開始的標誌

Elisp 是由符號表示式構成的 (即”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式。把游標移到閉括號後,之後按下ctrl+j(以後簡寫為`C-j`)

(+ 3 (+ 1 2))

游標放到最後的括號後,按下C-j 就會輸出 6。

C-j 會在buffer中插入當前運算的結果

C-xC-e 則會在emacs最底部顯示結果,也就是被稱作”minibuffer”的區域。為了避免把我們的buffer填滿無用的結果,我們以後會一直用C-xC-e

setq 可以將一個值賦給一個變數

(setq my-name "Bastien")

C-xC-e 輸出 “Bastien” (在 mini-buffer 中顯示)

insert 會在游標處插入字串:

(insert "Hello!")

C-xC-e 輸出 “Hello!”

在這裡我們只傳給了insert一個引數”Hello!”, 但是我們也可以傳給它更多的引數,比如2個:

(insert "Hello" " world!")

C-xC-e 輸出 “Hello world!”

你也可以用變數名來代替字串

(insert "Hello, I am " my-name)

C-xC-e 輸出 “Hello, I am Bastien”

你可以把s式嵌入函式中

(defun hello () (insert "Hello, I am " my-name))

C-xC-e 輸出 hello

現在執行這個函式

(hello)

C-xC-e 輸出 Hello, I am Bastien

函式中空括號的意思是我們不需要接受任何引數。但是我們不能一直總是用my-name這個變數。所以我們現在使我們的函式接受一個叫做”name”的引數。

(defun hello (name) (insert "Hello " name))

C-xC-e 輸出 hello。

現在我們呼叫這個函式,並且將”you”作為引數傳遞

(hello "you")

C-xC-e 輸出 “Hello you”

成功!

現在我們可以休息一下

下面我們在新的視窗中新建一個名為 *test* 的buffer:

(switch-to-buffer-other-window "*test*")

C-xC-e 這時螢幕上會顯示兩個視窗,而游標此時位於test buffer內。用滑鼠單擊上面的buffer就會使游標移回。或者你可以使用 C-xo 使得游標跳到另一個視窗中

你可以用 progn 命令將s式結合起來:

(progn
  (switch-to-buffer-other-window "*test*")
  (hello "you"))

C-xC-e 此時螢幕分為兩個視窗,並且在test buffer中顯示”Hello you”

現在為了簡潔,我們需要在每個s式後面都使用C-xC-e來執行,後面就不再說明了。記得可以用過滑鼠或者C-xo回到scratch這個buffer。

清除當前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!
" "visitor")

%s 是字串佔位符,這裡被”visitor”替代。
是換行符。

現在我們用格式化的方法再重寫一下我們的函式:

(defun hello (name)
  (insert (format "Hello %s!
" name)))

(hello "you")

我們再用let新建另一個函式:

(defun greeting (name)
  (let ((your-name "Bastien"))
    (insert (format "Hello %s!

I am %s."
                    name       ; the argument of the function
                    your-name  ; the let-bound variable "Bastien"
                    ))))

之後執行:

(greeting "you")

有些函式可以和使用者互動:

(read-from-minibuffer "Enter your name: ")

這個函式會返回在執行時使用者輸入的資訊

現在我們讓greeting函式顯示你的名字:

(defun greeting (from-name)
  (let ((your-name (read-from-minibuffer "Enter your name: ")))
    (insert (format "Hello!

I am %s and you are %s."
                    from-name ; the argument of the function
                    your-name ; the let-bound var, entered at prompt
                    ))))

(greeting "Bastien")

我們讓結果在另一個視窗中顯示:

(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!

I am %s." your-name from-name))
    (other-window 1)))

測試一下:

(greeting "Bastien")

第二節結束,休息一下吧。

我們將一些名字存到列表中:

(setq list-of-names `("Sarah" "Chloe" "Mathilde"))

car 來取得第一個名字:

(car list-of-names)

cdr 取得剩下的名字:

(cdr list-of-names)

push 把名字新增到列表的開頭:

(push "Stephanie" list-of-names)

注意: carcdr 並不修改列表本身, 但是 push 卻會對列表本身進行操作。這個區別是很重要的: 有些函式沒有任何副作用(比如car),但還有一些卻是有的 (比如 push)。

我們來對list-of-names列表中的每一個元素都使用hello函式:

(mapcar `hello list-of-names)

greeting 改進,使的我們能夠對list-of-names中的所有名字執行:

(defun greeting ()
    (switch-to-buffer-other-window "*test*")
    (erase-buffer)
    (mapcar `hello list-of-names)
    (other-window 1))

(greeting)

記得我們之前定義的 hello 函式嗎? 這個函式接受一個引數,名字。mapcar 呼叫 hello, 並將list-of-names作為引數先後傳給hello

現在我們對顯示的buffer中的內容進行一些更改:


(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)) 將游標移到buffer的開始,(search-forward "Hello") 查詢字串”Hello”, (while x y) 當x返回某個值時執行y這個s式。 當x返回nil (空), 退出迴圈。

(replace-hello-by-bonjour)

你會看到所有在test buffer中出現的”Hello”字樣都被換成了”Bonjour”

你也會得到以下錯誤提示: “Search failed: Hello”.

如果要避免這個錯誤, 你需要告訴 search-forward 這個命令是否在buffer的某個地方停止查詢, 並且在什麼都沒找到時是否應該不給出錯誤提示。

(search-forward "Hello" nil t) 可以達到這個要求:

  • nil 引數的意思是: 查詢並不限於某個範圍內
  • t 引數的意思是: 當什麼都沒找到時,不給出錯誤提示

在下面的函式中,我們用到了s式,並且不給出任何錯誤提示:

(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)

給這些名字上個色:

(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))

這個函式使用了 re-search-forward: 和查詢一個字串不同,你用這個命令可以查詢一個模式,即正規表示式

正規表示式 “Bonjour (.+)!” 的意思是:

字串 "Bonjour ", 之後跟著
一組           |  \( ... \) 結構
任意字元       |  . 的含義
有可能重複的   |  + 的含義
之後跟著 "!" 這個字串

準備好了?試試看。

(boldify-names)

add-text-properties 可以新增文字屬性, 比如文字樣式

好的,我們成功了!

如果你想對一個變數或者函式有更多的瞭解:

  • C-h v 變數 回車
  • C-h f 函式 回車

閱讀Emacs Lisp官方文件:

C-h i m elisp 回車

線上閱讀Emacs Lisp文件:

https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html

感謝以下同學的建議和反饋:

  • Wes Hardaker
  • notbob
  • Kevin Montuori
  • Arne Babenhauserheide
  • Alan Schmitt

via http://learnxinyminutes.com

相關文章