在Common Lisp中,eq
、eql
、equal
和 equalp
有什么区别?我知道它们中的一些检查类型,另一些检查跨类型,但是哪个是哪个呢?何时使用其中一个比另一个更好?
在Common Lisp中,eq
、eql
、equal
和 equalp
有什么区别?我知道它们中的一些检查类型,另一些检查跨类型,但是哪个是哪个呢?何时使用其中一个比另一个更好?
Common Lisp: Equality Predicates 中提到:
(eq x y)
当且仅当x
和y
是指向同一个对象时返回true。如果其参数是
eq
,或者是相同类型、相同值的数字,或者是表示相同字符的字符对象,则eql
谓词返回true。如果其参数是结构相似(同构)的对象,则
equal
谓词返回true。大致的经验法则是,如果两个对象的打印表现形式相同,则它们相等。如果它们相等,如果它们是字符并满足char-equal,即忽略字符的大小写和某些其他属性;如果它们是数字并具有相同的数值,即使它们的类型不同;或者如果它们所有组件都是
equalp
,则两个对象是equalp
。
以下是来自同一页面的一些示例:
(eq 'a 'b) is false.
(eq 'a 'a) is true.
(eq 3 3) might be true or false, depending on the implementation.
(eq 3 3.0) is false.
(eq 3.0 3.0) might be true or false, depending on the implementation.
(eq #c(3 -4) #c(3 -4))
might be true or false, depending on the implementation.
(eq #c(3 -4.0) #c(3 -4)) is false.
(eq (cons 'a 'b) (cons 'a 'c)) is false.
(eq (cons 'a 'b) (cons 'a 'b)) is false.
(eq '(a . b) '(a . b)) might be true or false.
(progn (setq x (cons 'a 'b)) (eq x x)) is true.
(progn (setq x '(a . b)) (eq x x)) is true.
(eq #\A #\A) might be true or false, depending on the implementation.
(eq "Foo" "Foo") might be true or false.
(eq "Foo" (copy-seq "Foo")) is false.
(eq "FOO" "foo") is false.
(eql 'a 'b) is false.
(eql 'a 'a) is true.
(eql 3 3) is true.
(eql 3 3.0) is false.
(eql 3.0 3.0) is true.
(eql #c(3 -4) #c(3 -4)) is true.
(eql #c(3 -4.0) #c(3 -4)) is false.
(eql (cons 'a 'b) (cons 'a 'c)) is false.
(eql (cons 'a 'b) (cons 'a 'b)) is false.
(eql '(a . b) '(a . b)) might be true or false.
(progn (setq x (cons 'a 'b)) (eql x x)) is true.
(progn (setq x '(a . b)) (eql x x)) is true.
(eql #\A #\A) is true.
(eql "Foo" "Foo") might be true or false.
(eql "Foo" (copy-seq "Foo")) is false.
(eql "FOO" "foo") is false.
(equal 'a 'b) is false.
(equal 'a 'a) is true.
(equal 3 3) is true.
(equal 3 3.0) is false.
(equal 3.0 3.0) is true.
(equal #c(3 -4) #c(3 -4)) is true.
(equal #c(3 -4.0) #c(3 -4)) is false.
(equal (cons 'a 'b) (cons 'a 'c)) is false.
(equal (cons 'a 'b) (cons 'a 'b)) is true.
(equal '(a . b) '(a . b)) is true.
(progn (setq x (cons 'a 'b)) (equal x x)) is true.
(progn (setq x '(a . b)) (equal x x)) is true.
(equal #\A #\A) is true.
(equal "Foo" "Foo") is true.
(equal "Foo" (copy-seq "Foo")) is true.
(equal "FOO" "foo") is false.
(equalp 'a 'b) is false.
(equalp 'a 'a) is true.
(equalp 3 3) is true.
(equalp 3 3.0) is true.
(equalp 3.0 3.0) is true.
(equalp #c(3 -4) #c(3 -4)) is true.
(equalp #c(3 -4.0) #c(3 -4)) is true.
(equalp (cons 'a 'b) (cons 'a 'c)) is false.
(equalp (cons 'a 'b) (cons 'a 'b)) is true.
(equalp '(a . b) '(a . b)) is true.
(progn (setq x (cons 'a 'b)) (equalp x x)) is true.
(progn (setq x '(a . b)) (equalp x x)) is true.
(equalp #\A #\A) is true.
(equalp "Foo" "Foo") is true.
(equalp "Foo" (copy-seq "Foo")) is true.
(equalp "FOO" "foo") is true.
一些额外的注意事项:
大多数CL函数在没有指定测试时隐式使用EQL
另请参阅STRING-EQUAL,=和TREE-EQUAL
EQ的核心通常是指针比较
一个粗略的指南:
要比较... 使用...
对象/结构体 EQ
NIL EQ(但NULL函数更简洁且可能更便宜)
T EQ(或只是该值,但这样你不关心类型)
精确数字 EQL
浮点数 =
字符 EQL或CHAR-EQUAL
列表,CONS,序列 EQ(如果您想要完全相同的对象) EQUAL(如果您只关心元素)
字符串 EQUAL(区分大小写),EQUALP(不区分大小写) STRING-EQUAL(如果将符号混合在其中)
树(列表的列表) TREE-EQUAL(具有适当的:TEST参数)
请注意,为了效率,通常是EQ >> EQL >> EQUAL >> EQUALP。
本文参考自此处和我的老师的课件
eq 用于测试其参数(表示相同的计算机内存块)是否为同一符号。
例如:
(eq ‘A ‘B) NIL
(eq ‘RAM ‘RAM) T
(eq (cons 'a 'b) (cons a' b')) ; 这是因为两次调用 cons 会分别分配不同的内存块
eql 先使用 eq 进行比较,如果不相等,则尝试检查是否是相同类型和值的数字。
例如:
(eql 4 4.0) NIL
(eql 4 4) T
现在请注意一个区别:
(eq 4.0 4.0) NIL; 因平台而异, 可以参考第一个(被接受的)答案
(eql 4.0 4.0) T; 参数的类型和值相同
在某些实现中,(eq 4.0 4.0) 可能返回 true,因为标准未规定实现是否应该在内存中仅保留数字和字符的一个副本(就像它对符号所做的那样)。通常情况下不要在数字和字符上使用 eq,除非你确切知道自己在做什么。
equal 是一个更加“稳妥”的比较函数。可以将其视为告诉您两个对象是否看起来相同(结构相似或同构)。对于一般的相等性比较,这可能是您想要使用的运算符。对于数字、字符和符号,它的行为类似于 eql,但对于列表(conses)和字符串,它查看它们的元素是否相同。
例如:
(equal 4 4) T
(equal (+ 2 2) 4) T
现在请注意一个区别:
(eql (cons 'a 'b) (cons 'a 'b)) NIL
(equal (cons 'a 'b) (cons 'a 'b)) T ; equal 通常对于打印出相同结果的事物会返回 true
equalp 类似于 equal,只是更加高级。数字比较不考虑类型。字符和字符串比较不区分大小写。
例如:
(equalp (cons 'a 'b) (cons 'a 'b)) T ;与 equal 相同
现在请注意一个区别:
equal(4 4.0) 返回NIL
equalp(4 4.0) 返回T ; 由于equalp不区分数字类型,所以返回T