elisp語法

Table of Contents

  1. 執行elisp代碼
  2. 註釋
  3. 基本數值運算
    1. 加減乘除、取餘、指數
    2. 測試
    3. 浮點整數轉換
    4. 字符串數值轉換
  4. 基本邏輯、關係運算
    1. TRUE/FALSE
    2. 邏輯運算
    3. 數值關係運算
    4. 字符串比較運算
    5. 等價測試euqal
  5. 變量
    1. 全局變量
    2. 局部變量
  6. 打印消息
    1. message
    2. insert
    3. print
    4. prin1
    5. princ
  7. 緩衝和文件操做
    1. 建立buffer
    2. 獲取buffer名字
    3. 切換buffer
    4. 關閉buffer
    5. 文件操做
  8. 條件
    1. if-then-else
    2. when
    3. unless
    4. cond
  9. progn
  10. 循環
    1. while
    2. mapcar
    3. mapc(foreach)
    4. dolist
    5. dotimes
  11. catch/throw error/user-error
    1. 錯誤退出
  12. Sequence,list,Vector
    1. Sequence
    2. Vector
    3. list
  13. 函數定義
  14. 指令定義
  15. 參考

如下內容均在emacs *scratch*中測試html

執行elisp代碼

  1. 方法一:把光標(點位)置於表達式閉括號右側,執行"M-x eval-last-sexp"(或"C-x C-e")
  2. 方法二:選擇待執行的代碼塊,執行"M-x eval-region"
  3. 方法三:鍵入"M-x eval-defun",執行光標所在函數(只可執行由defvar和defcustom實現的代碼)
  4. 方法四:鍵入"M-x eval-expression",在minibuffer中編輯執行代碼
  5. 方法五:鍵入"M-x eval-buffer",執行整個buffer
  6. 方法六:鍵入"M-x load-file",加載執行整個文件
  7. 方法七:鍵入"M-x ielm",啓用elisp Shell編輯執行代碼

註釋

格式爲西文分號加空格,即";; ",可在任意一行的任何位置開始註釋。express

;; test code
(+ 3 4)  ;; test code

基本數值運算

加減乘除、取餘、指數

(+ 4 5 1)  ;; 10
(- 9 2 3)  ;; 4
(* 2 3 3)  ;; 18
(/ 7 2)    ;; 3,取整
(/ 7 2.0)  ;; 3.5,除法
(% 7 4)    ;; 3,取餘
(expt 2 3) ;; 8

測試

(integerp 3.) ;; t,3.表示整數,3.0表示小數
(floatp 3.)   ;; nil
(floatp 3.0)  ;; t

:函數名字以p結尾表示測試,返回對(t)或錯(nil)數組

浮點整數轉換

(float 3)      ;; 3.0
(truncate 3.3) ;; 3,捨棄小數部分
(floor 3.3)    ;; 3,向下取整
(ceiling 3.3)  ;; 4,向上取整
(round 3.4)    ;; 3,四捨五入

字符串數值轉換

(string-to-number "3")
(number-to-string 3)

基本邏輯、關係運算

TRUE/FALSE

elisp中 沒有布爾類型
只有 nil空列表 () 表示 FALSE
其它全部都爲 TRUE ,即 tapp

邏輯運算

與或非less

(and t nil)  ;; nil 與運算
(or  t nil)  ;; t 或運算
(not t)      ;; nil 非運算

數值關係運算

(< 3 4)  ;; 小於
(> 3 4)
(<= 3 4)
(>= 3 4)

(= 3 3)  ;; t,比較兩數是否相等
(= 3 3.000000000000000000001) ;; t,可見elisp將超過精度小數部分捨棄

(/= 3 4) ;; t, 比較兩數是否不相等

字符串比較運算

(equal "abc" "abc")         ;; t

;; string-equal比較字符串專用函數
(string-equal "abc" "abc")  ;; t
(string-equal "abc" "Abc")  ;; nil,大小寫問題
(string-equal "abc" 'abc)   ;; t,可用於比較字符串和符號

等價測試euqal

一般使用equal進行等價測試,它比較兩個測試對象是否具備相同類型和值。ide

(equal "abc" 'abc) ;; nil

不等價測試,運算"/="只針對數值,對於字符串和其它數據類型無效,可以使用not來否認equal達到目標。函數

not (equal "abc" 'abc) ;; t

變量

全局變量

setq用於設置全局變量,且變量無需聲明。oop

(setq x 1)           ;; x = 1
(setq a 3 b 2 c 7)   ;; a = 3, b = 2, c = 7,批量賦值

局部變量

let用於設置局部變量。測試

  • 形式一this

    (let (var1 var2 ...) body)

    body由elisp表達式組成,且其最後一條表達式的返回值做爲let的返回值。

    (let (a b)
      (setq a 3)
      (setq b 4)
      (+ a b)) ;; 7
  • 形式二

    (let ((var1 val1) (var2 val2) ...) body)

    此方法無需在body中使用setq爲變量賦值,更方便。

    (let ((a 3) (b 4))
      (+ a b)) ;; 7

打印消息

message

(message FORMAT-STRING &rest ARGS)

打印格式化字符串到 Message ,可經過"M-x view-echo-area-message" 或 "C-h e" 查看

(message "age is: %d " 16)

FORMAT-STRING字符串格式化:

format explanation
%s 表示字符串,相似princ(後面介紹)
%d 十進制數值(%o 八進制, %x 十六進制)
%X 大寫的十六進制
%e 指數表示的數值
%f 小數點表示的數值
%g 使用指數或小數點表示數值,以字符較少爲準
%c 以字符方式打印數值
%S 打印任何對象的S-表達式,相似prin1(後面介紹)

insert

(insert &rest ARGS)

在當前buffer的光標位置插入字符串。

(insert "xyz")

print

(print OBJECT &optional PRINTCHARFUN)

打印lisp OBJECT(整數、浮點、字符、字符串、列表、符號等),輸出能夠被read函數讀回,
Optional參數能夠是一個buffer或函數。

setq xbuff (generate-new-buffer "*my output*"))

(print "something" xbuff)

(switch-to-buffer xbuff )

with-output-to-temp-buffer

(with-output-to-temp-buffer BUFNAME &rest BODY)

將標準輸出綁定到緩衝區BUFNAME,執行BODY,則首先會清空BUFNAME,而後在BUFNAME中顯示結果,
不會將結果顯示在當前緩衝區。

(setq xbuff (generate-new-buffer "*my output*"))

(with-output-to-temp-buffer xbuff
  ;; this is inserted in current buffer
  (insert "xyz")

  ;; this is printed in buffer xbuff
  (print "abc"))

(switch-to-buffer xbuff )

prin1

(prin1 OBJECT &optional PRINTCHARFUN)

相似print,可是不會換行。

(prin1 '("x" "y")) ;; ("x" "y")

princ

(princ OBJECT &optional PRINTCHARFUN)

既不換行也不打印字符串中的分隔符。

(princ '("x" "y")) ;; (x y),未打印」引號「分割符

緩衝和文件操做

建立buffer

  1. with-temp-buffer

    (with-temp-buffer &rest BODY)

    建立臨時buffer並像progn(後面介紹)執行BODY。

    (setq myStr "big text")
    
    (with-temp-buffer
        (insert myStr)
        ;; manipulate the string here
        ;; print whole buffer content
        (message "%s" (buffer-string)))
    :大多數時候應該使用該函數建立新buffer,能夠節省建立buffer的代碼、切換到它作些事,
    或關閉它,而後恢復當前緩衝區。
  2. generate-new-buffer
    建立新buffer並返回。

    ;; 設置新buffer名字,若是名字以空格開始,則撤銷會被禁止
    (setq newBufName " xyz")
    
    ;; 建立一個新buffer並保存在一個變量中,以便後續切換或關閉
    (setq newBuf (generate-new-buffer newBufName))
    
    ;; 把新buffer設置爲當前buffer且不可見,全部的插入等操做都適用於它
    (set-buffer newBuf)
  3. get-buffer-create

    (get-buffer-create BUFFER-OR-NAME)
    • 返回新buffer可是不會切換到它,需使用set-buffer(後面介紹)切換
    • BUFFER-OR-NAME能夠是字符串或buffer
    • 若是BUFFER-OR-NAME存在,則僅僅返回;若不存在則新建
    • 若是字符串以空格開頭,則撤銷操做被禁止

      ;; 確保字符串是惟一的且以空格開頭
      (setq newBuf (get-buffer-create " xyz"))
      (set-buffer newBuf)

獲取buffer名字

  1. buffer-name

    ;; 獲取當前buffer名字
    (buffer-name)
  2. buffer-file-name

    ;; 獲取buffer文件的完整路徑,若無文件返回nil
    (buffer-file-name)

切換buffer

  1. with-current-buffer
    建立臨時buffer,在函數執行完後會自動返回原buffer。

    (witch-curent-buffer myBuf
       ;; insert or delete text here
    )
  2. set-buffer
    切換到指定buffer,但緩衝區不可見。

    (save-current-buffer
      ;; switch to myBuf
      (set-buffer myBuf)
      ;; do stuff, such as insert/delete text
    )
  3. switch-to-buffer
    切換到指定buffer,但不用在Lisp代碼中,通常用於切換到可見緩衝區。

關閉buffer

kill-buffer

;; 關閉指定buffer
(kill-buffer myBuffer)

文件操做

  1. find-file

    ;; 打開文件,返回一個buffer
    (find-file "~/test.txt")
  2. save-buffer

    ;; 保存當前buffer,保存buffer關聯文件
    (save-buffer)
  3. write-file

    ;; 至關於「另存爲」
    (write-file "~/new.txt")
  4. append-to-file

    ;; 在指定位置追加文本
    (append-to-file 100 200 "~/test.txt")   ;; 在位置100~200追加內容

條件

if-then-else

(if test body)
或
(if test true_body false_body)

(if (< 3 2) 7 8) ;; 8

;; 若沒有false_body則返回nil
(if (< 3 2) (message "yes")) ;; nil

when

若是if語句中不須要else部分,則可以使用when語句,形如:

(when condition expr1 expr2 ...)
;;等價於
(if condition (progn expr1 expr2 ...) nil)

unless

若是if語句中不須要then部分,則可以使用unless語句,形如

(unless condition expr1 expr2 ...)
;;等價於
(if condition nil expr1 expr2 ...)

cond

相似於C語言中的switch語句。

  • cond每一個clause必須是list,且list的car值是condition,剩餘部分是body-forms

    (condition body-forms...)
  • 若condition非nil,則執行相應的body-forms,並將該clause最後一條body-form的返回值做爲
    cond的返回值,退出cond條件語句,忽略剩餘clasues。
  • 若全部的condition爲nil,則表示全部的clause fail,cond返回nil。

    (cond
         ((numberp x) x)
         ((stringp x) x)
    
         ((bufferp x)
             (setq temporary-hack x)         ;; multiple body-forms
             (buffer-name x))                ;; in one clause
    
         ((symbolp x) (symbol-value x)))
  • 可能cond語句的全部條件都測試爲nil,但咱們不但願cond返回nil,能夠用t做爲cond最後一個
    clasue,如(t "default")。

    (setq a 5)
    
    (cond
        ((eq a 'hack) 'foo)
        (t "default"))

    :任何條件結構均可以由if或cond表示,區別在於風格,例

    (if a b c)
     ≡
    (cond (a b) (t c))

progn

有時候咱們須要將多個表達式放在一個塊中做爲一個表達式,相似C中的塊`{…}`,在elisp中由函數progn實現。

(progn body...)

通常在if語句中使用

(if something
    (progn  ;; true
    ...
    )
    (progn  ;; else
    ...
    )
)

progn返回其body最後一個表達式的返回值。

(progn 3 4) ;; 4

循環

while

(while test body)

body由至少一個lisp表達式組成。

(setq x 0)

(while (< x 4)
   (message (format "number is %d" x))
   (setq x (1+ x)))         ;; number is 3

(let ((mylist '(a b c)))
     (while mylist
       (message "%s" (pop mylist)
       (sleep-for 1)))          ;; pop用於減小list

mapcar

(mapcar FUNCTION SEQUENCE)

應用 FUNCTION,遍歷 SEQUENCE 元素,返回一個list。輸入 SEQUENCE 多是一個list、vector、
bool-vector或字符串,但輸出爲 list,且該 list 長度和 SEQUENCE 同樣。

(mapcar '1+ [3 4 5])   ;; (4 5 6),將1加在每一個vector元素並返回一個list
(mapcar '1+ '(3 4 5))  ;; (4 5 6),將1加在每一個list元素並返回一個list

1+ 是一個lisp函數,它將參數加一併返回,如`(1+ 2)`返回3
在mapcar函數中使用,必須在函數名前加引用
1+ 是一個函數,因此須要加引用,即 `'1+` 或 `(quote 1+)`

(mapcar 'car '((1 2) (3 4) (5 6))) ;; (1 3 5),取出每一個元素list的第一個元素

mapcar 一般結合 lambda 使用,例如

(mapcar
    (lambda (x) (elt x 0))
    [[1 2] [3 4]])          ;; (1 3),獲取每一個元素vector的第一個元素

lambda 定義一個「匿名函數「,可使你在代碼中定義一個函數,形如

(lambda (args) body)
(lambda (x y) (+ x y)) ;; 取兩個參數相加,返回他們的和

(mapcar
  (lambda (x) (+ x 1))
  (list 1 2 3 4))      ;; (2 3 4 5),每一個list元素加一

mapc(foreach)

mapc相似mapcar,可是返回nil。

(mapc 'my-update-html-footer
 (list
  "~/file1.html"
  "~/file2.html"
  "~/file3.html"
  )) ;; 使用函數遍歷list中每一個文件

dolist

(dolist (VAR LIST) BODY)        ;; 遍歷list返回nil
(dolist (VAR LIST RESULT) BODY) ;; 返回 RESULT

(let (
     (xlist (number-sequence 97 122)) ;; list 97 to 122
     )
  (dolist (n xlist) (insert n)))

dolist和mapc主要區別

  • dolist使用表達式,mapc使用函數
  • dolist只做用於list,mapc做用於list和vector

dotimes

(dotimes (VAR COUNT) BODY ...)        ;; 循環指定次數,從0開始計數,不包括COUNT,返回nil
(dotimes (VAR COUNT RESULT) BODY ...) ;; 返回 RESULT

dotimes在使用升序計數的循環遍歷中很是有用

(dotimes (i 4)
  (insert (number-to-string i)))

catch/throw error/user-error

退出函數:map,loop.

(catch 'tagname body)

catch執行body,返回body最後一個表達式的返回值。若body中包含(throw …),且被調用,則返回throw傳遞的值。

(throw 'tagname value)

退出函數或跳出相應tagname的map或loop

(defun test-exit-f ()
  "example. using catch/throw to exit function"
  (interactive)
  (catch 'aaa
    (if (y-or-n-p "exit?")
       (progn
         (message "existing")
         (throw 'aaa 3)        ;; 若是yes,馬上退出,並返回3
         )
       (progn
         (message "went on")
         4 ;; return 4
         )))))

錯誤退出

可調用error或user-error

(defun test-exit-f
  "example"
  (interactive)
  (if (y-or-n-p "invoke user-error to exit?")
     (user-error "Error, because: %s" "you said so!")
        (progn
         (message "went on")
         )))

Sequence,list,Vector

Sequence

sequence和Array實際上不是elisp數據類型

  • 函數文檔中的sequence類型,表示它多是list、vector或string
  • 函數文檔中的array類型,表示它多是vector或string
  • list和vector都是有序值序列,每一個元素能夠是任何類型

list和vector區別

  • vector:全部元素訪問時間相同
  • vector的長度不能改變

  • List:元素訪問時間和位置成比例,相似C中鏈表
  • list的長度能夠改變,經過增刪list第一個元素實現

Vector

相似Java中的數組對象

  • Vector是有序值序列
  • 每一個元素能夠是任何類型
  • 元素值可修改
  • 元素個數不能改變
  • 讀寫元素隨機訪問時間相同

  • 建立Vector
    • make-vector

      (make-vector 5 0)  ;; [0 0 0 0 0],建立一個長度爲5個元素的向量,且每一個元素初始化爲0
    • vector

      (vector a b ...)  ;; 建立一個包含元素a,b,...的向量
    • [a b …]
      以該方式建立的向量元素不會在建立時被執行
  • 填充Vector

    (fillarray array val) ;; 使array中的全部值爲val,相似從新賦值
    
    (setq aa [3 4 5])
    (fillarray aa nil)    ;; [nil nil nil]
  • 獲取Vector長度

    (length (Vector 7 4 5)) ;; 3
  • 獲取元素

    (aref array n)   ;; 返回array索引爲n的元素
    (elt sequence n) ;; 返回sequence索引爲n的元素
    強調 :emacs文檔說起「array",你能夠認爲是"vector"或"string"
    說起"sequence",你能夠認爲是"list"或"array"
    若已知是vector,則使用aref更好更快
  • 修改元素

    ;; 將 ARRAY索引爲IDX的元素值替換爲NEWELT,返回NEWELT
    (aset ARRAY IDX NEWELT)
  • 嵌套Vector

    ;; Vector能夠以任何方式嵌套
    [[1 2] [3 4]]     ;; 2 by 2 matrix
  • 鏈接Vectors,轉換list爲Vector

    ;; 鏈接任何sequence類型,返回一個vector
    (vconcat sequence1 sequence2 ...)
    
    (vconcat [3 4] '("a" "b")) ;; [3 4 "a" "b"]
  • 轉換Vector爲list

    ;; 鏈接任何sequence類型,返回一個list
    (append sequence1 sequence2 ...)

    :若想返回一個proper list,最後一個元素必須是list或nil.

    (append [1 2 3] [4 5])     ;; (1 2 3 . [4 5]),improper list
    (append [1 2 3] [4 5] nil) ;; (1 2 3 4 5),proper list

list

  1. 建立list

    (list a b ...)

    若不想元素被執行,可寫做

    '(a b ...)
    ;;等價於
    (quote (list a b ...))
    
    (setq mylist '(a b c))
    (message "%S" mylist)
    
    (make-list LENGTH INIT) ;;建立長度爲LENGTH的list,全部元素初始化爲INIT
  2. 空list
    在elisp中,空list等價於nil

    '()
    ≡
    (list)
    ≡
    nil
  3. list of number

    (number-sequence n m step) ;; 返回從n到m,步長爲step的list
    (number-sequence n m)      ;; 默認step爲1
    (number-sequence n)        ;; 返回只有一個元素n的list
    
    (number-sequence 0 9 3)    ;; (0 3 6 9)
  4. length

    (length list)
  5. 獲取一個元素

    (car list)        ;; 獲取第一個元素
    (nth n list)      ;; 獲取第n個元素
    (car (last list)) ;; 獲取最後一個元素
    ;;list索引(下標)從0開始
  6. 獲取子list

    (cdr list)       ;; 獲取第二個元素到最後一個元素
    (nthcdr n list)  ;; 獲取第n個元素到最後一個元素
    (butlast list n) ;; 獲取除最後n個元素的元素
  7. 前置鏈接list(prepend to list)

    (cons x list)     ;; 將x加到list前面,返回一個新list
    
    (cons "a" (list "c" "d")) ;; ("a" "c" "d")
  8. 鏈接list

    (append sequence1 sequence2 ...) ;; 鏈接任何類型sequence,返回list
  9. 修改元素

    (push list)       ;; 在變量前加一個元素,返回新list
    (pop list)        ;; 移除變量第一個元素,返回移除元素
    (nbutlast list n) ;; 移除變量最後n個元素,返回變量新值
    (setcar list x)   ;; 用x替換list的第一個元素,返回x
    (setcdr list x)   ;; 用x替換list除第一個元素外剩餘元素,返回x
  10. list轉換爲string

    (mapconcat 'number-to-string '(1 2 3) ",") ;; "1,2,3"
    
    (format "%s" '(1 "two" 3)) ;; "(1 two 3)
    
    (substring (format "%s" '(1 "two" 3)) 1 -1) ;; "1 two 3"

函數定義

(defun function_name (param1 param2 ...) "doc_string" body)

函數返回值爲 body 中最後一個表達式的返回值.

(defun myFunction ()
   "testing"
   (message "Yay!"))
  1. 可選參數(&optional)
    若是你的函數須要可選擇的參數,只需在參數列表中加&optional選項,在該選項後的剩餘參數均是可選的.

    ;; 定義一個有兩個可選參數的函數,可選參數爲cc和dd
    (defun myfun (aa bb &optional cc dd)
        "test optional arguments"
        (insert aa bb cc dd))
    
    (myfun "1" "2" "3" "4")             ;; 當optional parameter 沒有給出,則它的值爲nil
    
    (myfun "myaa" "mybb" nil "mydd")    ;; 若你不關心某個可選參數,可將其置爲nil
  2. 不定量參數(&rest)
    要指定未指定數量的參數,可在最後一個參數添加&rest name;name的值能夠是一個list,若沒有給出,則是nil

    (defun ff (aa bb &rest cc)
      "test rest arguments"
      (message "%s" cc))      ;; cc is a list
    
    (ff "1" "2" "3" "4")      ;; ("3" "4")

指令定義

  • 指令就是一個函數,用戶能夠經過調用execute-extended-command(即M-x)執行
  • 若一個函數是一個指令,咱們說這個函數可用於交互(interactive)
  • 若使一個函數可交互,在函數的doc string後面增長代碼(interactive)

    (defun yay ()
      "Insert 「Yay!」 at cursor position."
      (interactive)
      (insert "yay!"))

    執行以上代碼,而後可使用"M-x yay"調用該函數.

參考

  1. https://learnxinyminutes.com/docs/elisp/
  2. http://ergoemacs.org/emacs/elisp_basics.html
相關文章
相關標籤/搜索