相等

相等性 属于 Common Lisp

1 个练习

关于 相等性

Common Lisp 和其他语言一样,有一套决定两个对象是否“相同”的规则。这些规则定义了四个层级,每个层级都有一个执行该级别检查的函数。这些层级按从最严格到最宽松的顺序排列。

eq

第一层是对象同一性。这种相等性用函数eq检查。被检查相等性的两个对象必须是同一个对象:

(eq 'apples 'apples)  ; => T
(eq 'apples 'oranges) ; => NIL

(eq '(a b c) '(a b c) ; => NIL (these two lists have the same contents but are not the same list)
(let ((list1 '(a b c)) (list2 list1)) 
  (eq list1 list2))   ; => T (these two lists are the same list)

eql

第二层加入了数字和字符的相等性。这种相等性用函数eql检查。检查的方式取决于实参的类型:

  • 任何两个eq的对象也都是eql的
  • 数字的类型和值都相同时,它们就是eql的
  • 字符表示同一个字符时,它们就是eql的。
(eql 1 1)     ; => T
(eql 1 1/1)   ; => NIL (one number is an integer, the other a rational)
(eql #\c #\c) ; => T
(eql #\c #\C) ; => NIL (case is different)

你可能会奇怪,为什么数字和字符不用eq比较对象同一性。Common Lisp 标准允许实现自行选择是否复制数字和字符。因此0和0可能不是eq的,因为它们可能是数字0的不同实例。

equal

第三层检查结构上的相似性。这种相等性用equal检查。检查的方式取决于实参的类型:

  • 符号按eq的方式比较
  • 字符和数字按eql的方式比较
  • 如果 cons 的元素都是equal的,那么这些 cons 就是equal的。这一比较是递归进行的。
  • 如果字符串和位向量的元素都是eql的,那么它们就是equal的
  • 其他类型的数组按eq的方式比较
  • 如果路径名功能等价,那么它们就是equal的。(在构成路径名各组成部分的字符串的大小写敏感性方面,这里为依赖具体实现的行为留有余地。)
  • 其他任何类型的对象按eq的方式比较
(equal '(a (b c)) '(a (b c)))         ; => T (conses are equal if their contents are equal)
(equal "hello" "hello")               ; => T
(equal "hello" "HELLO")               ; => NIL
(equal #(1 2 3) #(1 2 3))             ; => NIL (arrays are equal only if eq)
(equal #P"foo/bar.md" #P"foo/bar.md") ; => T (pathnames are equal if "functionally equivalent"

equalp

第四层,也是最宽松的一层相等性,用equalp检查。检查的方式取决于类型:

  • 如果两个对象是equalp的,那么它们就是equalp的
  • 如果数字的值相同,即使类型不同,它们也是equalp的
  • 字符和字符串的比较不区分大小写
  • 如果 cons 的元素都是equalp的,那么这些 cons 就是equalp的。这一比较是递归进行的。
  • 如果数组的维数相同、各维的大小也相同,并且每个元素都是equalp的,那么这些数组就是equalp的。
  • 如果结构体的类相同、槽相同,并且两个结构体之间每个对应的槽都是equalp的,那么这些结构体就是equalp的。
  • 如果两个哈希表的:test函数相同、键相同(按该:test函数比较),并且这些键对应的值按equalp比较也相同,那么这两个哈希表就是equalp的。
(equalp 1 1.0)                       ; => T
(equalp #\c #\C)                     ; => T
(equalp "hello" "HELLO")             ; => T
(equalp #(1 2 3) #(1.0 2.0 3.0))     ; => T (arrays contain elements which are `equalp`)
(equal #S(TEST :SLOT1 'a :SLOT2 'b) 
       #S(TEST :SLOT1 'a :SLOT2 'b)) ; => T (structures of the same class with slots that have values which are `equalp`)

类型专属函数

以上都是“通用”的相等性函数。按定义,它们适用于任何类型。当你编写通用代码、直到运行时才知道要比较的对象的类型时,这会很有用。不过,当你知道被比较的类型时,一般认为使用类型专属的相等性函数是“更好的风格”。例如用string=而不是equal。这些函数会在相关概念中介绍和讨论。

通过 GitHub 编辑 该链接会在新窗口或标签页中打开

学习 相等性