Emacs: Elisp 手册学习手稿
- TAG: 编码
使用 lisp 模式 M-x lisp-interaction-mode 或 Ielm 模式 M-x inferior-emacs-lisp-mode
Lisp 数据类型
(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