Emacs: Elisp 手册学习手稿

使用 lisp 模式 M-x lisp-interaction-mode 或 Ielm 模式 M-x inferior-emacs-lisp-mode

Lisp 数据类型

参考:Lisp Data Types

(message "%s" emacs-build-time)
"(26937 51891 419712 0)"
(message "%s" emacs-version)
"30.2"
(message "%s" emacs-major-version)
"30"
(message "%s" emacs-minor-version)
"2"
(prin1 emacs-repository-version)
nilnil
(current-buffer)
#<buffer *scratch*>

(let ((a  (list 1)))
  (setcdr a a))
(1 . #0)
(setq a (list 1))
(1)
(cdr a)
nil
()

;;; 编程类型
;;;; 整数类型
(fixnump 1)
t
(bignump 1)
nil
(message "%d" most-negative-fixnum)
"-2305843009213693952"
(message "%d" most-positive-fixnum)
"2305843009213693951"
(> most-positive-fixnum 99999)
t
-1
-1
1
1
1.
1
+1
1

;;;; 浮点类型
;; 浮点数看作一个分数与一个 10 的幂的组合
1500.0
+15e2
15.0e+2
+1500000e-3
;;;; 字符类型
?A
65
?B
66
?a
97
?\(
40
?\\
92
?\a ; C-g
7
?\b ; DEL
8
?\t
?\n
?\s ; 空格符
32
?\  ; 多加一个空格,以便与后续文本分隔
32

?\r
13
?\+ ; 等价 ?+ ; 没有特殊转义含义的字符前面加反斜杠是允许的
;; ()[]\;" 前必须加反斜杠。 |'`#., 前建议加
?\|
124
?|
124
?\`
96
;; 非 ASCII
?\N{LATIN SMALL LETTER A WITH GRAVE} ; 非 ASCII,如有名称。名称中的空格可以替换为任意非空空白序列(如换行)
224
?à
224
?\N{U+E0}
224
?\u00e0
224
?\U000000e0
224
?\340 ; 八进制
224
?\101 ; A
65
?\001 ; C-a
;; 控制字符
?\^I  ; C-i
9
?\^i  ; C-i
9
?\C-i ; C-i
9
?\C-b ; C-b
2

ELISP> ?\^? ; DEL 字符视为 ?
127
 (#o177, #x7f, ?\C-?)
ELISP> ?\C-?
127
 (#o177, #x7f, ?\C-?)

;; META 字符语法
?\M-A ; meta 在高位
134217793

ELISP> ?\M-\101
134217793
 (#o1000000101, #x8000041)
ELISP> ?A
65
 (#o101, #x41, ?A)
ELISP> ?\M-\C-b ; C-M-b
134217730
 (#o1000000002, #x8000002)
ELISP> ?\C-\M-b
134217730
 (#o1000000002, #x8000002)
ELISP> ?\M-\002
134217730
 (#o1000000002, #x8000002)
ELISP> ?\C-b
2
 (#o2, #x2, ?\C-b
;; 其他字符修饰符
?\C-\S-o ; Shift-Ctrl-o
33554447
?\S-\C-o
33554447
;; hyper \H- ; super \s- ; alt \A-
?\H-\M-\A-x ; Alt-Hyper-Meta-x
155189368

;;;; 符号类型
1+                  ; 符号名称为 ‘1+                    ;   (而非 ‘+1’ — 后者是整数)。
\+1                 ; 符号名称为 ‘+1                    ;   (可读性较差)。
\(*\ 1\ 2\)         ; 符号名称为 ‘(* 1 2)’ (更差的名称)。

;;;; 序列类型
;; list
;; array: string, vector, char-table, bool-vector

(length "aa")
2
(length '(aa))
1
(length ["aa" "bb"])
2
;; 一般,不可能两次读取到同一序列对象;空列表除外,() === nill

;;;; Cons 单元与列表类型
;; cons cell:是包含两个存储位的对象,分别称为 CAR 位和 CDR 位
;; list:是由一系列 Cons 单元串联而成的结构:每个 Cons 单元的 cdr 位,
;;   要么存放着下一个 Cons 单元,要么存放着空列表。
;; atoms:不是 Cons 单元的对象

;; list
(A 2 "A")
()
nil
("A ()")
((A B C))            ; 只有一个元素的列表
;; CAR 与 CDR 这两个名称来源于 Lisp 历史。
;;   最早的 Lisp 运行在 IBM 704 计算机上,该机将字分为地址部分(address)和减量部分(decrement);
;;    CAR 是提取寄存器地址部分内容的指令,CDR 是提取寄存器减量部分内容的指令。

;; 三元素列表 (rose violet buttercup) 等价于 (rose . (violet . (buttercup)))
    --- ---      --- ---      --- ---
   |   |   |--> |   |   |--> |   |   |--> nil
    --- ---      --- ---      --- ---
     |            |            |
     |            |            |
      --> rose     --> violet   --> buttercup

;; 关联列表类型 alist
;; 它的元素都是 cons 单元
(setq alist-of-colors
      '((rose . red) (lily . white) (buttercup . yellow)))
((rose . red) (lily . white) (buttercup . yellow))
;; 这个变量是一个包含三个元素的关联列表。在第一个元素中,rose 是键,red 是值。

;;;; 数组类型
;; 四种数组:strings字符串、vectors向量、bool-vectors布尔向量和 char-tables字符表。
;; 布尔向量只能存放 t 或 nil。
;; 所有 Emacs Lisp 数组都是一维的。

;;;; 字符串类型
;; 字符串的读取语法是一对双引号,中间包含任意数量的字符
"\""
"foo\r"
"foo\^Ibar"
"foo    bar"
"\N{LATIN SMALL LETTER A WITH GRAVE}"
"à"
"\u00e0"
"à"
;; 转义换行符,lisp 在读取字符串时会忽略转义换;转义空格同样会被忽略
"ab\
cd\ e"
"abcde"
;; 字符串中的非 ASCII 字符
"\xe0"
"\340"

;; 字符串中的不可打印字符
"\t, \C-a"
"       , "
"\M-A"
"\301"
;; 字符串中的文本属性
;; #("characters" property-data...) 属性数据由零个或多个元素组成,每三个为一组
#("foo bar" 0 3 (face bold) 3 4 nil 4 7 (face italic))
;; ‘foo bar’ 的字符串,其中:前三个字符拥有 face 属性,值为 bold(粗体);最后三个字符拥有 face 属性,值为 italic(斜体)。(第四个字符没有任何文本属性)。

;;;; 向量类型
[1 "two" (three)]      ; 包含三个元素的向量
;;;; 字符表类型
;; 字符表 = 用字符当下标的数组
;; 打印长这样:#^[...]
;; 用途:给每个字符绑定信息(大小写、语法、显示、分类等)
(setq my-char-table (make-char-table 'foo "默认值")) ; 创建一个空字符表
(aset my-char-table ?a 'letter-a) ; 给字符 'a' 赋值
(aref my-char-table ?a) ; 取 'a' 的值
letter-a

;;;; 布尔向量类型
;; 布尔向量 是一种一维数组,其元素只能是 t(真)或 nil(假)。
;; 打印表示形式与字符串类似,但开头会以 ‘#&’ 后跟长度值
(make-bool-vector 3 t)
(make-bool-vector 3 nil)
ELISP> ?\C-g
7
 (#o7, #x7, ?\C-g)
ELISP> ?\C-@
0
(#o0, #x0, ?\C-@)
;; 布尔向量的长度不是 8 的整数倍,
;;    其打印表示会显示额外的元素,但这些额外元素实际上无任何意义。
(equal #&3"\377" #&3"\007") ; 
t

;;;; 哈希表类型
(make-hash-table)
#s(hash-table)
;;;; 函数类型
(setq f 'list)
list
(funcall f 'x 'y 'z) ; 获取一个函数对象,然后使用原始函数 funcall 和 apply 来调用
(x y z)
(apply '+ 1 2 '(3 4)) ; 其行为与 funcall 基本一致,但最后一项需为一个对象列表
10
;;;; 宏类型
;; 生成另一段 Lisp 表达式
(defmacro inc2 (var1 var2)
    (list 'progn (list 'inc var1) (list 'inc var2)))
inc2
(macroexpand '(inc2 r s))
(progn (inc r) (inc s))
;;;; 原语函数类型
;; 子程序、内置函数,打印时#<xxx>
(symbol-function 'car) ; 获取符号绑定的函数
#<subr car>
(subrp (symbol-function 'car)) ; subrp 是否为原始函数
t
;;;; 闭包类型
;; 是将函数定义转化为函数值时生成的函数对象
;;;; 记录类型
;; 作用创建不属于 Emacs 内置类型的自定义类型对象
;;;; 类型描述符
;; 用于存放类型相关信息的记录。
;;;; 类型说明符
;; 是表示一种类型的表达式。为函数接口提供文档等。
;; 类型说明符可分为基本类型与复合类型。
(and 1 2) ; 只要其中一个生成nil,则返回nil。否则返回最后一个参数值。
2
(and 1 nil)
nil
(and nil 1)
nil
(or 1 2) ; 只要其中一个生成true,则返回true 内容。否则返回最后一个参数值。
1
(or nil 2)
2
(integer -10 10)
;;;; 自动加载类型
autoload
(defun doctor ()xxxx)
;;;; 终结器类型
;; 当返回的终结器对象在垃圾回收后变为不可达时,Emacs 就会调用与该终结器关联的函数。


;;; 编辑类型
;;;; buffer 类型
;; point 缓冲区一个指定位置
(current-buffer)
#<buffer 2026-03-31-elisp-note.org>
;;;; 标记类型
;; 标记包含两个部分:一个指向缓冲区,一个指向位置。
(point-marker)
#<marker at 5776 in 2026-03-31-elisp-note.org>
;;;; window 类型
;; 用来显示缓冲区的屏幕区域。每个窗口有且只属于一个框架。
;; 任意时刻都有一个窗口被指定为 选中窗口,选中窗口一般会显示当前缓冲区
(selected-window)
#<window 6 on 2026-03-31-elisp-note.org>
;;;; frame 类型
;; 是一块包含一个或多个 Emacs 窗口的屏幕区域
(selected-frame)
#<frame 2026-03-31-elisp-note.org 0x11a172f40>
;;;; terminal 类型
;; 是一种能够显示一个或多个 Emacs 框架的设备
(get-device-terminal nil)
#<terminal 1 on jasperdeMacBook-Air.local>
;;;; 窗口配置类型
;;;; 框架配置类型
;;;; 进程类型
(process-list)
(#<process ielm>)
;;;; 线程类型
(all-threads)
(#<thread 0x10367a6c8>)
;;;; 互斥锁类型
(make-mutex "my-mutex")
#<mutex my-mutex>
;;;; 条件变量类型
(make-condition-variable (make-mutex))
#<condvar 0x13f57a688>
;;;; 流类型
;;;; 按键映射类型
;;;; 覆盖层类型
;;;; 字体类型
;;;; xwidget 类型


;;; 循环对象的读取语法
(prog1 nil
  (setq x '(#1=(a) b #1#)))
nil

(eq (nth 0 x) (nth 2 x))
t

(setq x '((a) b (a)))
((a) b (a))
(eq (nth 0 x) (nth 2 x))
nil

;;; 类型谓词
(+ 2 'a)
Wrong type argument: number-or-marker-p, a
(type-of 1)
integer
(type-of nil)
symbol
(type-of '(x))
cons
(type-of (record 'foo))
foo
(cl-type-of 1)
fixnum
(cl-type-of nil)
null
(cl-type-of (record 'foo))
foo

;;; 相等性谓词
(eq 1 1) ; 是否是同一对象
t
(eq "a" "a") ; 返回 nil 或 t
(eq '(1 2) '(1 2))
nil
(equal 'foo 'foo)
t
(equal 456 456) ; 是否组成部分相等
t
(equal "ad" "ad")
t
(eq '(1 2) '(1 2))
nil
(equal-including-properties "asdf"
                            (propertize "asdf" 'asdf t))
nil

;;; 可变性

;;; Emacs Lisp 对象的类型层次
;; list列表 类型派生自 sequence序列 类型,而序列类型本身又派生自 t。

数值

;;  Emacs 支持两种数值数据类型:整数、浮点数

;;; 整数基础
1
1

1.
1

+1
1

-1
-1

0
0

-0
0

;; 非十进制
#b101100
44
#o54
44
#x2c
44
#24r1k ; 合法进制 2 ~ 36
44

111111 ; -1 二进制补码
111011 ; -1 - 4, 4 的二进制为 100
;; fixnum 32位 2**29, 64位 2**61
(message "%d" most-positive-fixnum) ; 最大 fixnum
"2305843009213693951"
(princ most-negative-fixnum) ; 最小 fixnum
-2305843009213693952
(message "%d" integer-width)
"65536"

;;; 浮点数基础
;; 5 种浮点数写法完全等价
1500.0
+15e2
15.0e+2
+1500000e-3
.15e4
;; elisp 规定,对于不含指数的浮点数,小数点后必须至少有一位数字
1500. ; 是整数,非浮点数
(= -0.0 0) ; IEEE 浮点标准 --- 该标准规定,即便其他操作可区分 -0.0 和 0.0
t
(eql -0.0 0)
nil
(eq -0.0 0)
nil
(/ 0.0 0.0) ; 返回 NaN
0.0e+NaN
;; eql、equal、sxhash-eql、sxhash-equal 和 gethash
;;   这类非数值函数判断的是值是否不可区分,而非数值上是否相等
(eql 0.0 -0.0)
nil
(equal 0.0 -0.0)
nil
(= 0.0 -0.0)
t
;; 无穷大:‘1.0e+INF’ 和 ‘-1.0e+INF;; 非数值(NaN):‘0.0e+NaN’ 和 ‘-0.0e+NaN(isnan 1.0)
nil
(isnan 1) ; 报错 wrong-type-argument floatp 1 ;参数类型错误,需要 float,实际给的 1
(isnan 0.0e+NaN)
t
(frexp 1500.0) ; 传入参数 x 返回 (s . e), x = s * 2**e
(0.732421875 . 11)
(* 0.732421875 (expt 2 11))
1500.0
(frexp 0)
(0.0 . 0)
(frexp 1.0e+INF)
(1.0e+INF . 0)
(frexp 0.0e+NaN)
(0.0e+NaN . 0)
;; 给定数值型尾数 s 和整数型指数 e,该函数返回浮点数结果为 s * 2**e。
(ldexp 0.732421875 11)
1500.0
;; 该函数将 x2 的符号复制到 x1 的数值上,并返回结果。
(copysign 0.1 0.2)
0.1
(copysign 0.1 -0.0)
-0.1
;; 该函数返回 x 的二进制指数。
(logb 1) ; 若 x 是有限非零值,返回值为以 2 为底的对数,向下取整为整数;
0
(logb -1)
0
(logb -4)
2
(logb 10)
3
(logb 0)
-1.0e+INF
(logb 0.0e+NaN)
0.0e+NaN

;;; 数值类型谓词
(integerp 1) ; 检测参数是否为整数
t
(integerp "a")
nil
(floatp 0.0) ; 检测参数是否为浮点数
t
(floatp "a")
nil
(zerop 0) ; 参数必须是数值
t
(zerop 1)
nil
;; (zerop "a") ; (wrong-type-argument number-or-marker-p "a")
(bignump 1)
nil
(fixnump 1)
t
(numberp 1) ; 检测参数是否为数值(整数或浮点数均可)
t
(numberp 0.0)
t
(numberp "a")
nil
(natnump 1) ; 检测参数是否为非负整数
t
(natnump 0.0)
nil
(natnump 0)
t
(wholenump 0) ; 同 natnump
t

;;; 数值比较
;; = 采用纯数值比较
;; 而不是 eq、eql、equal 这类非数值比较谓词。
;;
;; 由于浮点数运算不精确,直接判断浮点数是否相等通常是不合适的。
;; 一般更好的做法是判断近似相等
(= 2 2) ; 判断所有参数是否数值上都相等
t
(= 0.0 0)
t
(eql 2 2) ; 同时按类型和数值比较数字
t
(eql 1.0 1)
nil
(eql 0.0 -0.0)
nil
(eql 0.0e+NaN 0.0e+NaN)
t
(= 0.0 -0.0)
t
(= 0.0e+NaN 0.0e+NaN)
nil
(/= 2 3) ; 判断两个参数是否数值不相等,不相等返回 t,相等返回 nil
t
(/= 2 2)
nil
(< 2 3) ; 每个参数是否严格小于后一个参数,是则返回 t,否则返回 nil
t
(<= 2 2) ; 每个参数是否小于等于后一个参数,是则返回 t
t
(> 2 3) ; 每个参数是否严格大于后一个参数,是则返回 t
nil
(>= 2 3) ; 每个参数是否大于等于后一个参数,是则返回 t
nil
(max 2) ; 返回所有参数中的最大值
2
(max 2 2.5 3.5)
3.5
(min 2 2.5 3.5) ; 返回所有参数中的最小值。
2
(abs -1) ; 返回参数的绝对值
1

;;; 数值转换
(float 1) ; 转换为浮点数
1.0
;; 将浮点数转换为整数,它们的舍入方式不同。
(truncate 1.2) ; 向零舍入
1
(truncate 1.9)
1
(truncate -1.2)
-1
(truncate -1.7)
-1
(floor 1.2) ; 向下舍入(向负无穷方向)。
1
(floor 1.7)
1
(floor -1.2)
-2
(floor -1.7)
-2
(floor 5.99 3) ; 指定了 divisor,该函数使用与 mod 对应的除法方式,向下舍入
1
(ceiling 1.2) ; 向上舍入(向正无穷方向)
2
(ceiling 1.7)
2
(ceiling -1.2)
-1
(ceiling -1.7)
-1
(round 1.2) ; 四舍五入到最近整数
1
(round 1.7)
2
(round -1.2)
-1
(round -1.7)
-2

;;; 算术运算
;; 这些函数均接受整数和浮点数参数,只要任意一个参数是浮点数,就返回浮点数。
(setq foo 4)
4
(1+ foo) ; 加 1,但它不会修改变量
5
(message "%s" foo)
"4"
(1- foo) ; 减 1
3
(+ 2 1.0 3) ; 对所有参数求和。无参数时返回 0。
6.0
(+)
0
(- 1) ; 单个参数:返回其相反数;
-1
(- 10 1 2 3 4) ; 多个参数:从第一个数依次减去后面所有数 
0
(-)
0