LISP - 相等谓词
Lisp - 相等谓词指南
Section titled “Lisp - 相等谓词指南”在许多语言中,检查两个事物是否相等是一个简单的 == 操作。在 Lisp 中,“相等”的概念更为细致和强大。Common Lisp 提供了一套相等谓词,每个谓词都有精确的含义,允许你选择所需的确切比较类型。理解它们之间的区别是编写正确且无 bug 的 Lisp 代码的关键。
主要谓词形成一个严格程度的层次结构,从最具体到最一般:
eq (most strict) -> eql -> equal -> equalp (most general)四个主要谓词
Section titled “四个主要谓词”| 谓词 | 检查内容 | 类比 |
|---|---|---|
eq | 同一性 - 它们是内存中的同一个对象吗? | 同一个人。 |
eql | 原语值 - 它是否 eq,或者是类型和值都相同的数字,或是相同的字符? | 一个人的社会安全号码。 |
equal | 结构等价性 - 它们具有相同的结构和内容吗? | 同卵双胞胎。 |
equalp | 宽容等价性 - 它们“看起来”一样吗,忽略大小写和数字类型? | 一个人和他们的带照片的身份证。 |
eq - 最严格的比较
Section titled “eq - 最严格的比较”eq 仅当其参数是内存中的同一个对象时才返回 T。它速度快但用途有限。
;; 具有相同名称的符号始终是同一个对象。(eq 'hello 'hello) ; => T
;; 两个内容相同的列表不是同一个对象。(eq (list 'a 'b) (list 'a 'b)) ; => NIL
;; 但指向列表的变量与列表是 `eq` 的。(let ((x (list 'a 'b))) (eq x x)) ; => T
;; 对于数字,`eq` 是实现相关的,不应依赖。(eq 1 1) ; => 可能是 T 或 NIL。请改用 `eql` 或 `=`。eql - 原语的首选
Section titled “eql - 原语的首选”eql 类似于 eq,但它也能可靠地按值比较数字(同类型)和字符。它是 case 默认使用的谓词。
(eql 'hello 'hello) ; => T (与符号的 eq 相同)(eql 10 10) ; => T (数字可靠)(eql 10 10.0) ; => NIL (类型不同)(eql #\a #\a) ; => T (字符)(eql #\a #\A) ; => NIL (区分大小写)equal - 用于比较结构
Section titled “equal - 用于比较结构”equal 递归地比较列表、向量和字符串的内容。如果它们具有相同的结构和元素,则返回 T。
;; 两个内容相同的列表是 `equal` 的。(equal (list 'a 'b) (list 'a 'b)) ; => T
;; 内容相同的字符串是 `equal` 的。(equal "hello" "hello") ; => T
;; 数字使用 `eql` 比较。(equal 10 10.0) ; => NILequalp - 最宽松的比较
Section titled “equalp - 最宽松的比较”equalp 类似于 equal,但更加宽松。它适用于比较来自外部源或用户输入的数据,这些数据可能在类型和大小写上不一致。
- 它按值比较数字,忽略类型(
10和10.0是equalp的)。 - 它以不区分大小写的方式比较字符和字符串。
;; 忽略数字类型。(equalp 10 10.0) ; => T
;; 字符串比较不区分大小写。(equalp "hello" "Hello") ; => T
;; 字符比较不区分大小写。(equalp #\a #\A) ; => T如何选择:最佳实践
Section titled “如何选择:最佳实践”- 对于数字,为了清晰起见,请使用数字专用的谓词:
=、/=、<、>。(eql 10 10.0)为假,但(= 10 10.0)为真。 - 对于符号,
eq是最快且最符合习惯的选择。 - 对于字符串,优先使用专用函数
string=(区分大小写) 和string-equal(不区分大小写)。它们能更明确地表达你的意图。 - 对于结构化数据(如列表或树),如果你需要精确的结构匹配,
equal是你的工具。 - 对于用户输入或模糊匹配,当你需要忽略大小写或数字类型差异时,
equalp是合适的。 - 当有疑问时,
eql是非结构化数据通用比较的安全默认选项。