本节介绍用于检验两个对象是否相等的函数。另有一些函数用于检验特定类型对象(如strings字符串)的内容是否相等,相关谓词可参见对应数据类型的说明章节。
若 object1 和 object2 是同一个对象,该函数返回 t,否则返回 nil。
如果 object1 和 object2 是名称相同的符号,它们通常是同一个对象 —— 但存在例外情况(创建与编入符号)。对于其他非数值类型(如列表、向量、字符串),即便内容或元素完全相同,它们也不一定满足 eq 相等:只有当它们是同一个对象时才会 eq 相等,这意味着修改其中一个的内容,另一个的内容也会同步发生相同的变化。
如果 object1 和 object2 是类型或数值不同的数字,那么它们不可能是同一个对象,eq 返回 nil。如果它们是值相同的定点数(fixnum),那么它们是同一个对象,eq 返回 t。如果它们是分别计算得到,但恰好值相同且属于非定点数类型,那么它们可能是同一个对象,也可能不是;eq 会返回 t 或 nil,取决于 Lisp 解释器创建的是一个对象还是两个对象。
如果 object1 或 object2 是带位置信息的符号,当 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。
同理,你的代码不应该修改字面量对象(比如给字面字符串添加文本属性),因为如果字节编译器把它们合并了,这样的修改可能会影响到其他内容相同的字面量对象。
如果 object1 和 object2 的组成部分相等,该函数返回 t,否则返回 nil。eq 用于判断参数是否为同一个对象,而 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
如果 object1 或 object2 包含带位置信息的符号,当 symbols-with-pos-enabled 为非-nil 时,equal 会将它们当作普通裸符号处理。否则,equal 会通过比较各个组成部分来判断两个带位置信息的符号是否相等。See 带位置信息的符号。
其他对象只有在满足 eq 时,才会被认为是 equal。例如,两个不同的缓冲区(buffer)即使文本内容相同,也永远不会被视为相等。
equal 的相等性是递归定义的;例如,给定两个 cons 单元 x 和 y,(equal x y) 返回 t,当且仅当下面两个表达式都返回 t:
(equal (car x) (car y)) (equal (cdr x) (cdr y))
因此,比较循环列表可能会引发深层递归并导致报错,还可能出现违反直觉的行为 —— 例如 (equal a b) 返回 t,而 (equal b a) 却触发错误。
该函数在所有场景下的行为均与 equal 一致,但额外要求:若两个字符串要判定为相等,必须具备完全相同的文本属性。
(equal "asdf" (propertize "asdf" 'asdf t))
⇒ t
(equal-including-properties "asdf"
(propertize "asdf" 'asdf t))
⇒ nil