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. 控制结构:条件分支
Elisp 中的条件分支结构有 if
, when
, unless
和 cond
。
参考:
https://www.gnu.org/software/emacs/manual/html_node/elisp/Conditionals.html
https://www.emacswiki.org/emacs/WhenToUseIf
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
此外,宏 dolist
和 dotimes
也能实现循环迭代。
参考: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-fixnum
和 most-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-e
和 float-pi
中。
函数名 | 说明 |
---|---|
integerp object |
测试 object 是不是整数 |
floatp object |
测试 object 是不是浮点数 |
numberp object |
测试 object 是不是数(即同时测试整数或浮点数) |
zerop number |
测试 number(整数或浮点数)是不是等于 0 |
wholenump number |
测试 number 是不是非负整数(整数 0 和正整数) |
函数名 | 说明 |
---|---|
= number1 number2 |
测试数字是否相等,它不会检测类型是否严格相同。如 (= 1.0 1) 会返回 t |
/= number1 number2 |
和函数 = 的含义相反,数字相等时返回 nil,否则返回 t |
< number1 number2 |
小于测试 |
<= number1 number2 |
小于等于测试 |
> number1 number2 |
大于测试 |
>= number1 number2 |
大于等于测试 |
函数名 | 说明 |
---|---|
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"
字符串的长度在字符串建立时就已固定不变,不可修改。可以使用 aref
和 aset
访问和修改字符串中的字符。
2.2.1. 构建字符串
构建字符串相关函数如表 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 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 |
---|---|
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 | | | | | |____________| |_____________| | | | |________________________________| | |_____________________________________________|
2.3.1. Sequences 通用函数
Sequences 通用和部分通用函数如表 8 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 | 实例 |
---|---|---|
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 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 |
---|---|
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 所示。
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:使用 boundp
和 fboundp
可以分别测试符号的“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 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 |
---|---|
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 所示。
函数名 | 说明 |
---|---|
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
来改变函数行为的方式已经过时,不推荐使用。
函数名 | 说明 |
---|---|
(advice-add SYMBOL WHERE FUNCTION &optional PROPS) | 需要知道想要修补的函数名字,使用 advice-remove 可恢复以前行为 |
(add-function WHERE PLACE FUNCTION &optional PROPS) | 需要知道想要修补的函数在哪里,使用 remove-function 可恢复以前行为 |
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
等都是特殊表达式),则根据特殊表达式自己的规则对列表进行求值。