2.8 相等性谓词

本节介绍用于检验两个对象是否相等的函数。另有一些函数用于检验特定类型对象(如strings字符串)的内容是否相等,相关谓词可参见对应数据类型的说明章节。

Function: eq object1 object2

object1object2 是同一个对象,该函数返回 t,否则返回 nil

如果 object1object2 是名称相同的符号,它们通常是同一个对象 —— 但存在例外情况(创建与编入符号)。对于其他非数值类型(如列表、向量、字符串),即便内容或元素完全相同,它们也不一定满足 eq 相等:只有当它们是同一个对象时才会 eq 相等,这意味着修改其中一个的内容,另一个的内容也会同步发生相同的变化。

如果 object1object2 是类型或数值不同的数字,那么它们不可能是同一个对象,eq 返回 nil。如果它们是值相同的定点数(fixnum),那么它们是同一个对象,eq 返回 t。如果它们是分别计算得到,但恰好值相同且属于非定点数类型,那么它们可能是同一个对象,也可能不是;eq 会返回 tnil,取决于 Lisp 解释器创建的是一个对象还是两个对象。

如果 object1object2 是带位置信息的符号,当 symbols-with-pos-enabled 为非-nil 时,eq 会将其视为无附加信息的原始符号(see 带位置信息的符号)。

(eq 'foo 'foo)
     ⇒ t

(eq ?A ?A)
     ⇒ t

(eq 3.0 3.0)
     ⇒ t or nil
;; 数值相等的浮点数可能是、也可能不是同一个对象

(eq (make-string 3 ?A) (make-string 3 ?A))
     ⇒ nil

(eq "asdf" "asdf")
     ⇒ t or nil
;; 内容相等的字符串常量可能是、也可能不是同一个对象

(eq '(1 (2 (3))) '(1 (2 (3))))
     ⇒ nil

(setq foo '(1 (2 (3))))
     ⇒ (1 (2 (3)))
(eq foo foo)
     ⇒ t
(eq foo '(1 (2 (3))))
     ⇒ nil

(eq [(1 2) 3] [(1 2) 3])
     ⇒ nil

(eq (point-marker) (point-marker))
     ⇒ nil

make-symbol 函数会返回一个未存入(uninterned)的符号,它与直接书写符号名得到的符号是不同的对象。即便名称相同,不同的符号也不会是 eq 的。See 创建与编入符号

(eq (make-symbol "foo") 'foo)
     ⇒ nil

Emacs Lisp 字节编译器可能会把完全相同的字面量对象(比如字面字符串)合并成指向同一个对象的引用。这会导致一个结果:经过字节编译的代码里这些对象用 eq 比较会相等,而同样代码在解释执行时却不相等。 因此,你的代码永远不应该依赖“内容相同的字面量对象用 eq 比较是否相等” 这一点,而应该使用下文介绍的、用于比较对象内容的函数,例如 equal。 同理,你的代码不应该修改字面量对象(比如给字面字符串添加文本属性),因为如果字节编译器把它们合并了,这样的修改可能会影响到其他内容相同的字面量对象。

Function: equal object1 object2

如果 object1object2 的组成部分相等,该函数返回 t,否则返回 nileq 用于判断参数是否为同一个对象,而 equal 会深入不同对象的内部,检查它们的元素或内容是否相同。 因此:如果两个对象满足 eq,则一定满足 equal;但反过来不一定成立。

(equal 'foo 'foo)
     ⇒ t

(equal 456 456)
     ⇒ t

(equal "asdf" "asdf")
     ⇒ t
(eq "asdf" "asdf")
     ⇒ nil

(equal '(1 (2 (3))) '(1 (2 (3))))
     ⇒ t
(eq '(1 (2 (3))) '(1 (2 (3))))
     ⇒ nil

(equal [(1 2) 3] [(1 2) 3])
     ⇒ t
(eq [(1 2) 3] [(1 2) 3])
     ⇒ nil

(equal (point-marker) (point-marker))
     ⇒ t

(eq (point-marker) (point-marker))
     ⇒ nil

equal 函数按值比较字符串和布尔向量。数值则使用 eql,同时比较类型和数值。list(列表)、cons 单元、vector(向量)、record(记录)、marker(标记)、char-table(字符表)、font object(字体对象)以及function object(函数对象)(闭包)2 会通过对其组成部分递归调用 equal 来进行比较。

字符串的比较区分大小写,但不考虑文本属性 — 它只比较字符串里的字符。See Text Properties。若要同时比较文本属性,请使用 equal-including-properties。出于技术原因,单字节字符串与多字节字符串 equal 相等的充要条件是:它们包含完全相同的字符编码序列,并且所有编码都在 0~127(ASCII)范围内。

(equal "asdf" "ASDF")
     ⇒ nil

如果 object1object2 包含带位置信息的符号,当 symbols-with-pos-enabled 为非-nil 时,equal 会将它们当作普通裸符号处理。否则,equal 会通过比较各个组成部分来判断两个带位置信息的符号是否相等。See 带位置信息的符号

其他对象只有在满足 eq 时,才会被认为是 equal。例如,两个不同的缓冲区(buffer)即使文本内容相同,也永远不会被视为相等。

equal 的相等性是递归定义的;例如,给定两个 cons 单元 xy(equal x y) 返回 t,当且仅当下面两个表达式都返回 t

(equal (car x) (car y))
(equal (cdr x) (cdr y))

因此,比较循环列表可能会引发深层递归并导致报错,还可能出现违反直觉的行为 —— 例如 (equal a b) 返回 t,而 (equal b a) 却触发错误。

Function: equal-including-properties object1 object2

该函数在所有场景下的行为均与 equal 一致,但额外要求:若两个字符串要判定为相等,必须具备完全相同的文本属性。

(equal "asdf" (propertize "asdf" 'asdf t))
     ⇒ t
(equal-including-properties "asdf"
                            (propertize "asdf" 'asdf t))
     ⇒ nil

Footnotes

(2)

不过,不同的函数对象之间,一般无法保证其相等性。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike