Emacs Lisp

Table of Contents

1. Emacs Lisp 简介

Emacs Lisp(简称 Elisp)是一种 Lisp 方言,在 Emacs 中广泛使用。

参考:
An Introduction to Programming in Emacs Lisp
GNU Emacs Lisp Reference Manual
Elisp Cookbook
Elisp入门(叶文彬)

1.1. 控制结构:条件分支

1.1.1. if

if 语法为:

(if condition then-form else-forms…)

下面是 if 的使用例子:

(if nil
    (print 'true)
  'very-false)
⇒ very-false

else-forms 可以省略,也可以直接写多个(即隐含着 progn 的意思),如:

(if nil
    (print 'true)  ; 这是 then 部分,如果需要多个语句,则需要使用 progn 包围
  (print "foo")    ; 这是 else 部分
  (print "bar")    ; 这也是 else 部分,这个语句和上个语句不需要使用 progn 包围
  )

1.1.2. when

when 语法为:

(when condition a b c)    ; 使用 when 相对 if 的好处是:直接写多个语句作为 then-form,而不需要 progn 包围

当你想要一个 if 有多个 then-form,同时不需要 else-forms 时,可以使用 when ,它相当于下面 if 语句:

(if condition (progn a b c))

1.1.3. unless

unless 语法为:

(unless condition a b c)

它相当于:

(if condition nil   ; then 部分是 nil
   a b c)           ; else 部分是 a b c

1.1.4. cond

如果你需要很多分支,则可以考虑使用 cond ,下面是它的一个例子:

(setq a 1)
(cond ((eq a 1) 'one)
      ((eq a 2) 'two)
      ((eq a 3) 'three)
      (t 'other))
⇒ one

1.2. 控制结构:循环

Elisp 中循环控制使用特殊表达式 while ,它的语法形式为:

(while condition
  forms...)

下面是它的一个例子:

(setq num 0)
     ⇒ 0
(while (< num 4)
  (princ (format "Iteration %d." num))
  (setq num (1+ num)))
     -| Iteration 0.
     -| Iteration 1.
     -| Iteration 2.
     -| Iteration 3.
     ⇒ nil

此外,宏 dolistdotimes 也能实现循环迭代。

参考:https://www.gnu.org/software/emacs/manual/html_node/elisp/Iteration.html

2. Programming Types

Elisp 中有两个通用的数据类型:一是 Programming Types ,它在大多数 Lisp 实现中都存在;二是 Editing Types ,它仅仅存在于 Emacs Lisp 中,如 Buffer Type, Marker Type, Window Type, Frame Type 等属于 Editing Types。本节介绍 Programming Types。

2.1. Integer 和 Floating Point

整数默认为 10 进制,可以用 #radixNUM 的形式表示其他进制的整数。其中 # 号是前缀, radix 是 2 到 36 进制,也可以为进制代号: b 表示二进制, o 表示八进制, x 表示十六进制。

如下面几种形式都会得到整数 44:

(print 44)
(print #b101100)    ; same as above
(print #o54)        ; same as above
(print #x2c)        ; same as above
(print #24r1k)      ; same as above

Elisp 能够处理的最大整数和最小整数分别保存在变量 most-positive-fixnummost-negative-fixnum 中。

Elisp 中,浮点数 1500.0 可以表示为 15e2,15.0e2,1.5e3,.15e4 等等形式。Emacs Lisp 中有三个特别浮点数:
1、正无穷:1.0e+INF,通过 (/ 1.0 0) 可得到;
2、负无穷:-1.0e+INF,通过 (/ -1.0 0) 可得到;
3、Not-a-number: 0.0e+NaN 或-0.0e+NaN,通过 (/ 0.0 0.0) 可得到。

两个浮点常量:自然常量和圆周率分别保存在变量 float-efloat-pi 中。

1, 2, 3 是一些常用的数字相关函数。

Table 1: Integer 和 Floating Point 测试函数
函数名 说明
integerp object 测试 object 是不是整数
floatp object 测试 object 是不是浮点数
numberp object 测试 object 是不是数(即同时测试整数或浮点数)
zerop number 测试 number(整数或浮点数)是不是等于 0
wholenump number 测试 number 是不是非负整数(整数 0 和正整数)
Table 2: 数字比较函数
函数名 说明
= number1 number2 测试数字是否相等,它不会检测类型是否严格相同。如 (= 1.0 1) 会返回 t
/= number1 number2 和函数 = 的含义相反,数字相等时返回 nil,否则返回 t
< number1 number2 小于测试
<= number1 number2 小于等于测试
> number1 number2 大于测试
>= number1 number2 大于等于测试
Table 3: 浮点数取整函数
函数名 说明
truncate number &optional divisor 向零方向取整。如 (truncate 1.2) 返回 1, (truncate -1.2) 返回-1。如果设置可选参数 divisor,则返回对 number/divisor 进行取整后的结果,如 (truncate 100.5 3) 会返回 33
floor number &optional divisor 向下取整
ceiling number &optional divisor 向上取整
round number &optional divisor 四舍五入取整
ftruncate number 向零取整,结果转为浮点数。如 (ftruncate 1.2) 返回浮点数 1.0(而不是整数 1)
ffloor number 向下取整,结果转为浮点数
fceiling number 向上取整,结果转为浮点数
fround number 四舍五入取整,结果转为浮点数

2.2. Char 和 String

字符(Char)在 Emacs 中是用“整数”表示的。

读入一个 alphanumeric 字符的方法是在字符前加一个问号 ? ,如 ?A 代表字符 A 。读入一个标点字符也可以用上面同样的方法,也可以在前面加一个转义字符 \ ,如 ?+?\+ 都代表字符 + ;但当有岐义时,一定要加个转义字符,如用 ?\( 代表字符 (

?\+       ; => 43
?+        ; => 43
?\(       ; => 40

另外,控制字符有一些固定的表达:

?\b       ; => 8 ; backspace, <BS>
?\t       ; => 9 ; tab, <TAB>
?\n       ; => 10 ; newline
?\v       ; => 11 ; vertical tab
?\f       ; => 12 ; formfeed character
?\r       ; => 13 ; carriage return, <RET>

字符串是“有序的字符数组”。注意,字符 \000 并不表示字符串结束(这和 C 语言不同),字符串中可以包含 \000

(print "123\000456")  ; => "123^@456"

字符串的长度在字符串建立时就已固定不变,不可修改。可以使用 arefaset 访问和修改字符串中的字符。

2.2.1. 构建字符串

构建字符串相关函数如表 4 所示。

Table 4: 构建字符串
函数名 实例说明
make-string count character (make-string 5 ?x) ; ⇒ "xxxxx"
string &rest characters (string ?a ?b ?c) ; ⇒ "abc"
substring string start &optional end (substring "abcdefg" 0 3) ; ⇒ "abc", (substring "abcdefg" -3 -1) ; ⇒ "ef"
substring-no-properties string &optional start end This works like substring but discards all text properties from the value.
concat &rest sequences (concat "abc" "-def") ; ⇒ "abc-def"

2.2.2. 分割字符串

使用函数 split-string string &optional separators omit-nulls 可以分割字符串,默认以 [ \f\t\n\r\v]+ 为分割符(保留在变量 split-string-default-separators 中)。如:

(split-string " two words ")                                   ; ⇒ ("two" "words")
(split-string " two words " split-string-default-separators t) ; ⇒ ("two" "words")
(split-string " two words " split-string-default-separators)   ; ⇒ ("" "two" "words" "")

2.2.3. 修改字符串

修改字符串相关函数如表 5 所示。

Table 5: 修改字符串
函数名 说明
store-substring string idx obj (store-substring "abcd" 0 ?x) ; ⇒ "xbcd"
  (store-substring "abcd" 1 "123") ; ⇒ "a123"
clear-string string 常用于清除包含密码的字符串

注:字符串是保存着字符的 array,所以也可以用 array 相关函数修改字符串(如 aset )。

2.2.4. 字符串比较

字符串比较相关函数如表 6 所示。

Table 6: 字符串比较
函数名 说明
string= string1 string2 测试字符串是否相同(总是区分大小写,case-fold-search 不会影响 char-equal 的行为)
  (string= "abc" "abc") ; ⇒ t
string-equal string1 string2 同上,string-equal 是 string=的别名
string< string1 string2 (string< "abc" "abd") ; ⇒ t
string-lessp string1 string2 同上,string-lessp 是 string<的别名
string-greaterp string1 string2 相当于(string-lessp string2 string1)
compare-strings string1 start1 end1 string2 start2 end2 &optional ignore-case 比较两字符串指定部分内容

2.2.5. 测试前后缀

string-prefix-p string1 string2 &optional ignore-case
This function returns non-nil if string1 is a prefix of string2; i.e., if string2 starts with string1. If the optional argument ignore-case is non-nil, the comparison ignores case differences.

string-suffix-p suffix string &optional ignore-case
This function returns non-nil if suffix is a suffix of string; i.e., if string ends with suffix. If the optional argument ignore-case is non-nil, the comparison ignores case differences.

2.2.6. 字符串转换

字符串转换相关函数如表 7 所示。

Table 7: 字符串转换
函数名 说明
prin1-to-string OBJECT &optional NOESCAPE 转换 object 为 string
read-from-string STRING &optional START END 转换 string 为 object
number-to-string number 转换数字为字符串
string-to-number string &optional base 转换字符串为数字
char-to-string character 转换字符为字符串
string-to-char string This function returns the first character in string. This mostly identical to (aref string 0)
byte-to-string BYTE (byte-to-string 65) ; ⇒ "A"
format string &rest objects (format "%-6d is padded on the right" 123) ; ⇒ "123 is padded on the right"
concat This function converts a vector or a list into a string.
vconcat This function converts a string into a vector.
append This function converts a string into a list.
downcase string-or-char 把字符串(或字符)转为小写
upcase string-or-char 把字符串(或字符)转为大写
capitalize string-or-char 转单词首字母为大写,(capitalize "THE 77TH-HATTED CAT") ⇒ "The 77th-Hatted Cat"

2.3. Sequences, Arrays, and Vectors

直接上个 Sequences, Arrays, Vectors 等的关系图:

               _____________________________________________
              |                                             |
              |          Sequence                           |
              |  ______   ________________________________  |
              | |      | |                                | |
              | | List | |             Array              | |
              | |      | |    ________       ________     | |
              | |______| |   |        |     |        |    | |
              |          |   | Vector |     | String |    | |
              |          |   |________|     |________|    | |
              |          |  ____________   _____________  | |
              |          | |            | |             | | |
              |          | | Char-table | | Bool-vector | | |
              |          | |____________| |_____________| | |
              |          |________________________________| |
              |_____________________________________________|

参考:https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequences-Arrays-Vectors.html#Sequences-Arrays-Vectors

2.3.1. Sequences 通用函数

Sequences 通用和部分通用函数如表 8 所示。

Table 8: Sequences 通用和部分通用函数
函数名 说明
sequencep object 测试 object 是不是 List 或 Array
length sequence 返回 sequence 长度
elt sequence index This function returns the element of sequence indexed by index.
  (elt [1 2 3 4] 2) ; ⇒ 3
  (elt '(1 2 3 4) 2) ; ⇒ 3
  注:这个函数是 aref(应用于 array)和 nth(应用于 list)的一般形式。
copy-sequence sequence 复制 sequence
reverse sequence 反转 sequence(得到一个新 sequence,不改变原 sequence),char-tables 不适应
  (setq x '(1 2 3 4)) ; ⇒ (1 2 3 4)
  (reverse x) ; ⇒ (4 3 2 1)
  x ; ⇒ (1 2 3 4)
nreverse sequence 反转 sequence(得到一个新 sequence,可能改变原 sequence),strings 不适应
sort sequence predicate 排序 sequence(得到一个新 sequence,可能改变原 sequence),适应 lists 和 vectors
  (setq nums '(1 3 2 6 5 4 0)) ; ⇒ (1 3 2 6 5 4 0)
  (sort nums '<) ; ⇒ (0 1 2 3 4 5 6)
  nums ; ⇒ (1 2 3 4 5 6) ; 原 sequence 被 sort 修改了!
  (setq nums (sort nums '<)) ; 推荐使用 sort 后,用 setq 设置原 sequence
2.3.1.1. seq.el 库(Emacs 25 中内置)

在 Emacs 25 中引入了 seq.el 库。这个库中的函数以 seq- 打头,可以操作 lists, strings 和 vectors,如表 9 所示。

Table 9: seq.el 库中的函数
函数名 说明 实例
seq-elt sequence index 返回 sequence 中第 index 个元素 (seq-elt [1 2 3 4] 2) ; ⇒ 3
seq-length sequence 返回 sequence 的元素个数 (seq-length [1 2 3 4]) ; ⇒ 4
seq-drop sequence n 返回去除了前 n 个元素的 sequence (seq-drop [1 2 3 4 5 6] 3) ; ⇒ [4 5 6]
seq-take sequence n 返回包含前 n 个元素的 sequence (seq-take '(1 2 3 4) 3) ; ⇒ (1 2 3)
seq-take-while predicate sequence Returns the members of sequence in order, stopping before the first one for which predicate returns nil. (seq-take-while (lambda (elt) (> elt 0)) '(1 2 3 -1 -2 4)) ; ⇒ (1 2 3)
seq-drop-while predicate sequence Returns the members of sequence in order, starting from the first one for which predicate returns nil. (seq-drop-while (lambda (elt) (> elt 0)) '(1 2 3 -1 -2 4)) ; ⇒ (-1 -2 4)
seq-do function sequence Applies function to each element of sequence in turn (presumably for side effects), and returns sequence.  
seq-map function sequence Returns the result of applying function to each element of sequence. (seq-map #'1+ '(2 4 6)) ; ⇒ (3 5 7)
seq-mapn function &rest sequences Returns the result of applying function to each element of sequences. (seq-mapn #'+ '(2 4 6) '(20 40 60)) ; ⇒ (22 44 66)
seq-filter predicate sequence Returns a list of all the elements in sequence for which predicate returns non-nil. (seq-filter (lambda (elt) (> elt 0)) [1 -1 3 -3 5]) ; ⇒ (1 3 5)
seq-remove predicate sequence Returns a list of all the elements in sequence for which predicate returns nil. (seq-remove (lambda (elt) (> elt 0)) [1 -1 3 -3 5]) ; ⇒ (-1 -3)
seq-reduce function sequence initial-value Returns the result of calling function with initial-value and the first element of sequence, then calling function with that result and the second element of sequence, then with that result and the third element of sequence, etc. (seq-reduce #'+ [1 2 3 4] 0) ; ⇒ 10
seq-some predicate sequence Returns the first non-nil value returned by applying predicate to each element of sequence in turn. (seq-some #'numberp ["abc" 1 nil]) ; ⇒ t
seq-find predicate sequence &optional default Returns the first element in sequence for which predicate returns non-nil. If no element matches predicate, the function returns default. (seq-find #'numberp ["abc" 1 nil]) ; ⇒ 1
seq-every-p predicate sequence Returns non-nil if applying predicate to every element of sequence returns non-nil. (seq-every-p #'numberp [2 4 6]) ; ⇒ t
seq-empty-p sequence Returns non-nil if sequence is empty. (seq-empty-p "") ; ⇒ t
seq-count predicate sequence Returns the number of elements in sequence for which predicate returns non-nil. (seq-count (lambda (elt) (> elt 0)) [-1 2 0 3 -2]) ; ⇒ 2
seq-sort function sequence Returns a copy of sequence that is sorted according to function  
seq-contains sequence elt &optional function Returns the first element in sequence that is equal to elt. (seq-contains '(symbol1 symbol2) 'symbol3) ; ⇒ nil
seq-position sequence elt &optional function Returns the index of the first element in sequence that is equal to elt. (seq-position '(a b c) 'b) ; ⇒ 1
seq-uniq sequence &optional function Returns a list of the elements of sequence with duplicates removed. (seq-uniq '(1 2 2 1 3)) ; ⇒ (1 2 3)
seq-subseq sequence start &optional end Returns a subset of sequence from start to end. (seq-subseq '[1 2 3 4 5] 1 3) ; ⇒ [2 3]
seq-concatenate type &rest sequences Returns a sequence of type type made of the concatenation of sequences. type may be: vector, list or string. (seq-concatenate 'list '(1 2) '(3 4) [5 6]) ; ⇒ (1 2 3 4 5 6)
seq-mapcat function sequence &optional type Returns the result of applying seq-concatenate to the result of applying function to each element of sequence. (seq-mapcat #'seq-reverse '((3 2 1) (6 5 4))) ; ⇒ (1 2 3 4 5 6)
seq-partition sequence n Returns a list of the elements of sequence grouped into sub-sequences of length n. (seq-partition '(0 1 2 3 4 5 6 7) 3) ; ⇒ ((0 1 2) (3 4 5) (6 7))
seq-intersection sequence1 sequence2 &optional function Returns a list of the elements that appear both in sequence1 and sequence2. (seq-intersection [2 3 4 5] [1 3 5 6 7]) ; ⇒ (3 5)
seq-difference sequence1 sequence2 &optional function Returns a list of the elements that appear in sequence1 but not in sequence2. (seq-difference '(2 3 4 5) [1 3 5 6 7]) ; ⇒ (2 4)
seq-group-by function sequence Separates the elements of sequence into an alist whose keys are the result of applying function to each element of sequence. Keys are compared using equal. (seq-group-by #'integerp '(1 2.1 3 2 3.2)) ; ⇒ ((t 1 3 2) (nil 2.1 3.2))
seq-into sequence type Converts the sequence sequence into a sequence of type type. type can be one of the following symbols: vector, string or list. (seq-into [1 2 3] 'list) ; ⇒ (1 2 3)
seq-min sequence Returns the smallest element of sequence. (seq-min [3 1 2]) ; ⇒ 1
seq-max sequence Returns the largest element of sequence. (seq-max [1 3 2]) ; ⇒ 3
seq-doseq (var sequence) body... This macro is like dolist, except that sequence can be a list, vector or string.  
seq-let arguments sequence body... This macro binds the variables defined in arguments to the elements of sequence.  

参考:https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequence-Functions.html

2.3.2. Array 可用函数

Array 可用函数如表 10 所示。

Table 10: Array 可用函数
函数名 说明
arrayp object 测试 object 是不是 Array
aref array index This function returns the indexth element of array.
  (aref "abcdefg" 1) ; ⇒ 98 ; 'b' is ASCII code 98.
aset array index object This function sets the indexth element of array to be object.
  (setq x "asdfasfd")
  (aset x 3 ?Z)
  x ; ⇒ "asdZasfd"
fillarray array object This function fills the array array with object, so that each element of array is object.
  (setq s "When in the course")
  (fillarray s ?-) ; ⇒ "------------------"

2.3.3. Vector 可用函数

Vector 可用函数如表 11 所示。

Table 11: Vector 可用函数
函数名 说明
vectorp object 测试 object 是不是 vector
vector &rest objects This function creates and returns a vector whose elements are the arguments, objects.
  (vector 'foo 23 [bar baz] "rats") ; ⇒ [foo 23 [bar baz] "rats"]
make-vector length object (make-vector 9 'Z) ; ⇒ [Z Z Z Z Z Z Z Z Z]
vconcat &rest sequences This function returns a new vector containing all the elements of sequences. The arguments sequences may be true lists, vectors, strings or bool-vectors.
  (vconcat [A B C] "aa" '(foo (6 7))) ; ⇒ [A B C 97 97 foo (6 7)]

2.4. 符号(Symbol)

符号(Symbol)是有唯一名字的对象。 Lisp 中的符号(Symbol)和其它语言中的标识符(Identifier)类似,不过符号可以存储多个值。 可以把符号当作变量使用,当作函数使用,或者当作属性列表使用。后面会介绍它有四个组成部分。

2.4.1. 符号的四个组成部分

Elisp 中每个符号有四个组成部分(Componentes,或称为 Cells):1、符号的名字;2、符号的值;3、函数;4、属性列表。 如表 12 所示。

Table 12: Elisp 符号的四个组成部分
Cell Setter Getter Tester Note
符号的名字(name cell)   symbol-name   The same as the symbol
符号的值(value cell) set, setq symbol-value boundp Stores the symbol's value
函数(function cell) fset symbol-function fboundp Stores function definition object, lisp macros, or other objects that can act as function
属性列表(plist cell) put, plist-put symbol-plist, get, plist-get   Hold a list of name/value pairs. Used for storing meta info about the symbol, such as function documetation, font face spec

说明 1:同一个符号即可以是变量(保存在“value cell”中),也可以是函数(保存在“function cell”中),它们不会冲突。比如 emacs-version 就是这样的例子(它既是变量名,也是函数名),可以通过上下文来知道到底使用哪个 cell,详情可参考节 5
说明 2:使用 boundpfboundp 可以分别测试符号的“value cell”和“function cell”是否被设置过。

2.4.1.1. fset, symbol-function

符号的第 3 个组成成分是函数(“function cell”)。它可以用 fset 来设置,用 symbol-function 来访问。如:

(fset 'my-foo (symbol-function 'length)) ; => #<subr length>
(funcall 'my-foo '(a b c d e))           ; => 5   ; 符号my-foo的“function cell”和length的相同

说明:上面例子中没有显式创建和 intern 符号 my-foo 的过程,这是因为 Lisp 读入符号时会自动 intern 这个符号,详情参见下节。

2.4.2. 创建符号

符号名可以由任何字符组成。符号名是唯一的,全局变量 obarray(它的类型也称为 obarray,所以 obarray 在不同语境中有不同含义)中保存着 emacs 中的所有符号。

当 Lisp 读入一个符号时,首先查找这个符号是否存在于 obarray 中,如果没有则会把这个符号加入到 obarray 中。这个查找并加入一个符号的过程称为 intern。函数 intern 可以查找或加入一个名字到全局变量 obarray 里(或其它一个数组里),返回对应的符号。

创建符号相关函数如表 13 所示。

Table 13: 创建符号相关函数
函数名 说明
intern 检测符号是否存在,不存在就创建符号(“value cell”和“function cell”为 void,“plist cell”为 nil)并加入符号到 obarray 中
intern-soft 检测符号是否存在,它不会自动创建和加入符号到 obarray 中
make-symbol 创建符号(“value cell”和“function cell”为 void,“plist cell”为 nil),它不会加入符号到 obarray 中
unintern 去除 obarray 中的符号
setq, set, defvar, defconst 创建符号,设置“name cell”为变量/常量名,设置“value cell”为指定内容
defun 创建符号,设置“name cell”为函数名,设置“function cell”为函数内容
defmacro 创建符号,设置“name cell”为宏名,设置“function cell”为宏内容(它也放在“function cell”中,故无法创建同名的函数和宏)

下面是几个简单测试:

(intern-soft "abc")    ; => nil    ; 如果系统中存在符号abc,则会返回abc
(intern "abc")         ; => abc    ; 会把符号abc加入到全局obarray中
(intern-soft "abc")    ; => abc    ; 上一步已经intern了符号abc
(unintern "abc")       ; => t      ; 去除obarray中的符号abc
(intern-soft "abc")    ; => nil

说明: Lisp 读入一个符号时会自动 intern 符号到 obarray 里,如果想避免,可以在符号前加上 #: (参考:Sharpsign 。如:

(intern-soft "xyz")    ; => nil    ; 如果系统中存在符号xyz,则会返回xyz
'xyz                   ; => xyz    ; 会自动intern符号xyz到obarray里
(intern-soft "xyz")    ; => xyz    ; 上一步已经intern了符号xyz
'#:xyzz                ; => xyzz   ; 由于符号xyzz前使用了 #: ,故不会自动intern
(intern-soft "xyzz")   ; => nil    ; 上一步没有intern符号xyzz
2.4.2.1. Keyword Symbol(以 : 开头)

如果符号名字以 : 开头,则这个符号称为 Keyword Symbol。它们可以看作是常量。

3. 正则表达式

3.1. 构造正则表达式

构造正则表达式相关函数如表 14 所示。

Table 14: 构造正则表达式
函数名 说明
regexp-quote string This function returns a regular expression whose only exact match is string.
  (regexp-quote "^The cat$") ; ⇒ "\\^The cat\\$"
  注:常用它来将^.*$等特殊字符转为普通字符。
regexp-opt strings &optional paren This function returns an efficient regular expression that will match any of the strings in the list strings.
  (regexp-opt '("abc" "abcd" "abcde"))
  "\\(?:abc\\(?:de?\\)?\\)"
  注:如果要匹配多个可选词,常用它来生成高效的正则表达式,而不是手工构造正则表达式。
regexp-opt-charset chars This function returns a regular expression matching a character in the list of characters chars.
  (regexp-opt-charset '(?a ?b ?c ?d ?e)) ; ⇒ "[a-e]"

3.2. 用正则表达式进行字符串搜索

用正则表达式进行字符串搜索的基本函数如表 15 所示。

Table 15: string-match
函数名 说明
string-match regexp string &optional start This function returns the index of the start of the first match for the regular expression regexp in string, or nil if there is no match.
  (string-match "quick" "The quick brown fox jumped quickly.") ; ⇒ 4
  注意:string-match 会修改 Match Data。
string-match-p regexp string &optional start 和 string-match 相同,但不会修改 Match Data。

访问 Match Data 相关函数如表 16 所示。

Table 16: 访问 Match Data 相关函数
函数名 说明
match-string count &optional in-string (setq mystr "The quick fox jumped quickly.")
  (string-match "\\(qu\\)\\(ick\\)" mystr)
  (match-string 0 mystr) ; ⇒ "quick" ; 整个正则匹配的内容
  (match-string 1 mystr) ; ⇒ "qu" ; 正则中第 1 个括号匹配的内容
  (match-string 2 mystr) ; ⇒ "ick" ; 正则中第 2 个括号匹配的内容
match-string-no-properties count &optional in-string This function is like match-string except that the result has no text properties.
match-beginning count (match-beginning 1) ; ⇒ 4 ; 'qu' is at index 4.
  (match-beginning 2) ; ⇒ 6 ; 'ick' is at index 6.
match-end count (match-end 1) ; ⇒ 6 ; 'qu' is at index 6.
  (match-end 2) ; ⇒ 9 ; 'ick' is at index 9.
match-data &optional integers reuse reseat This function returns a list of positions (markers or integers) that record all the information on the text that the last search matched.
set-match-data match-list &optional reseat This function sets the match data from the elements of match-list, which should be a list that was the value of a previous call to match-data.

3.3. 用正则表达式进行字符串替换

用正则表达式进行字符串替换如表 17 所示。

Table 17: 用正则表达式进行字符串替换
函数名 说明
replace-match replacement &optional fixedcase literal string subexp This function replaces the text in the buffer (or in string) that was matched by the last search. It replaces that text with replacement.
  把上次在 buffer 或字符串中找到的匹配替换为 replacement。
  可选项 string 用来区分是搜索在 buffer 中还是字符串中。string 为 nil 时表示搜索在 buffer 中,如果搜索中字符串中,则把字符串传给参数 string。
replace-regexp-in-string regexp rep string &optional fixedcase literal subexp start This function copies string and searches it for matches for regexp, and replaces them with rep. It returns the modified copy. If start is non-nil, the search for matches starts at that index in string, so matches starting before that index are not changed.
  用指定的正则表示式对字符串去搜索,将搜索到的模式用指定的字符串对原字符串进行替换。
  (replace-regexp-in-string "foo*" "fu" "Fight foo for food!")
  ⇒ "Fight fu fur fud!"

4. Functions

4.1. Advising Functions

使用 fset, defun 等方式可以重新定义一个同名函数,但这会使以前的函数被完全丢弃了。我们很可能仅仅想稍微调整一下函数行为,没必要完全重写整个函数。

Elisp 提供了另外的方式可以用来修改函数的行为,而不用完全重写它们,称为 Advising。 有两种方式实现 Advising:如果你知道函数的具体名字(下面的 SYMBOL 参数),那么就可以用 advice-add ;如果你不知道函数的名字,但知道它在哪里(下面的 PLACE 参数),则可以使用 add-function 如表 18 所示,其 WHERE 参数如表 19 所示。注:通过 defadvice 来改变函数行为的方式已经过时,不推荐使用。

Table 18: Elisp Advising
函数名 说明
(advice-add SYMBOL WHERE FUNCTION &optional PROPS) 需要知道想要修补的函数名字,使用 advice-remove 可恢复以前行为
(add-function WHERE PLACE FUNCTION &optional PROPS) 需要知道想要修补的函数在哪里,使用 remove-function 可恢复以前行为
Table 19: advice-add(或 add-function)的 WHERE 参数说明
WHERE 说明
:before Call FUNCTION before the old function
:after Call FUNCTION after the old function
:override This completely replaces the old function with the new one
:around Call FUNCTION instead of the old function, but provide the old function as an extra argument to FUNCTION.
:before-while Call FUNCTION before the old function and don't call the old function if FUNCTION returns `nil'
:before-until Call FUNCTION before the old function and only call the old function if FUNCTION returns `nil'
:after-while Call FUNCTION after the old function and only if the old function returned non-`nil'
:after-until Call FUNCTION after the old function and only if the old function returned `nil'
:filter-args Call FUNCTION first and use the result (which should be a list) as the new arguments to pass to the old function
:filter-return Call the old function first and pass the result to FUNCTION

4.1.1. advice-add 实例

指定 WHERE 参数为 :around 是最通用的形式。下面是它的一个例子:

(defun add100 (x)
  (+ x 100))

(defun his-tracing-function (orig-fun &rest args)
  (message "traced function called with args %S" args)
  (let ((res (apply orig-fun args)))
    (message "traced returned %S" res)
    res))

(advice-add 'add100 :around #'his-tracing-function)

(add100 2)

在执行函数 (add100 2) 时会输出:

traced function called with args (2)
traced returned 102

5. 求值规则

求值(Evaluation)是 Lisp 解释器的核心。一个要求值的 Lisp 对象被称为表达式(form)。

表达式可以分为三类:一是“符号(Symbol)”,二是“列表”,三是除符号和列表外的其它类型(可统称为“自求值表达式”)。

比如数字、字符串,向量等都属于自求值表达式。“自求值表达式”的求值规则最简单:其求值结果就是它自己。

5.1. 符号的求值规则

符号的求值结果是符号的值(即符号的“value cell”中保存的值)。

(message "%s" emacs-version)   ;  => "25.1.1"
(eval 'emacs-version)          ;  => "25.1.1"

5.2. 列表的求值规则

列表的求值可根据第一个元素的不同而分为函数调用、宏调用和特殊表达式(Special form)三种。

如果列表第一个元素是符号,则解释器会查找这个符号的“function cell”中的内容,如果其“function cell”中是另一个符号,则会继续查找这个符号的“function cell”,直到它不再是符号。这个过程称为“symbol function indirection”,如下面例子所示:

;; Build this function cell linkage:
;;   -------------       -----        -------        -------
;;  | #<subr car> | <-- | car |  <-- | first |  <-- | erste |
;;   -------------       -----        -------        -------
(symbol-function 'car)
     ⇒ #<subr car>
(fset 'first 'car)
     ⇒ car
(fset 'erste 'first)
     ⇒ first
(erste '(1 2 3))        ; Call the function referenced by erste.
     ⇒ 1

如果列表第一个元素是 Lisp 函数对象、byte-code 对象和原子函数时,这个列表也称为函数调用(funtion call)。对这样的列表求值时,会对列表中其它元素(从第二个元素开始从左往右)先求值,值的结果作为函数调用的真正参数。然后使用 apply 函数用这些参数调用函数。

如果列表第一个元素是一个宏对象,列表里的其它元素不会立即求值,而是根据宏定义进行扩展。如果扩展后还是一个宏调用,则会继续扩展下去,直到扩展的结果不再是一个宏调用为止。

如果列表第一个元素是特殊表达式(如 if , unwind-protect 等都是特殊表达式),则根据特殊表达式自己的规则对列表进行求值。

Author: cig01

Created: <2012-11-01 Thu>

Last updated: <2021-01-16 Sat>

Creator: Emacs 27.1 (Org mode 9.4)