Skip to content

LISP - 比较谓词

在 Lisp 中,谓词是一种测试条件并返回布尔值的函数。与许多其他语言不同,Lisp 的规范布尔值是 t 符号表示真,nil 符号表示假。这些谓词是控制 if、cond、when 和 unless 等程序流程结构的基础。

本教程涵盖两类主要的谓词:用于数值比较的谓词和用于检查任意 Lisp 对象之间相等性的谓词。

这些函数用于比较数值。Common Lisp 的一个强大特性是它们可以接受两个以上的参数并顺序执行比较。

谓词描述
<如果数字按单调递增顺序排列,则返回 t。例如,(< 1 3 5) 为 t。
>如果数字按单调递减顺序排列,则返回 t。例如,(> 5 3 1) 为 t。
<=如果数字按单调非递减顺序排列,则返回 t。例如,(<= 3 3 5) 为 t。
>=如果数字按单调非递增顺序排列,则返回 t。例如,(>= 5 5 3) 为 t。
=如果所有数值参数的值都相等,则返回 t。例如,(= 5 5.0) 为 t。
/=如果没有两个数值参数的值相等,则返回 t。例如,(/= 1 2 3) 为 t。

让我们在 SBCL (Steel Bank Common Lisp) 这样的现代 Common Lisp 环境中看看这些函数的实际应用。

;; main.lisp
;; 基础的两个参数比较
(print (< 3 5)) ; T
(print (> 3 5)) ; NIL
;; 多个参数比较
(print (< 10 20 30)) ; T
(print (>= 50 25 25 1)) ; T
;; 数字的相等性检查
(print (= 10 10 10)) ; T
(print (= 10 10.0)) ; T (类型不同,但值相同)
;; 不等性检查
(print (/= 5 3 5)) ; NIL (因为第一个和第三个相等)
(print (/= 5 4 3)) ; T
T
NIL
T
T
T
T
NIL
T

选择正确的相等性谓词是 Lisp 中的常见任务,对正确性至关重要。它们在比较对象的严格程度上有所不同。

谓词描述与用例
eq同一性:如果参数是内存中的同一个对象,则返回 t。速度非常快。用于比较符号或检查两个变量是否指向完全相同的对象。
eql非数字的同一性,数字的值相等性:类似于 eq,但如果参数是相同类型和值的数字,也返回 t。这是 case 的默认谓词。
equal结构相等性:递归比较列表、字符串和其他结构的内容。如果它们具有相似的结构和等效的内容,则返回 t。速度较慢,但对于结构化数据更直观。
equalp宽松相等性:类似于 equal,但更宽松。它忽略字符串/字符中的大小写和数字类型差异(例如,(equalp 5 5.0) 为 t)。
;; main.lisp
(let ((list-a (list 'x 'y))
(list-b (list 'x 'y)))
;; EQ 检查内存中的同一个对象
(print (eq 'hello 'hello)) ; T (符号是唯一的)
(print (eq list-a list-b)) ; NIL (它们是两个不同的列表对象)
(print (eq list-a list-a)) ; T (是同一个对象)
(terpri) ; 换行以提高可读性
;; EQL 类似于 EQ,但适用于相同类型的数字
(print (eql 10 10)) ; T
(print (eql 10 10.0)) ; NIL (不同类型)
(terpri)
;; EQUAL 检查结构相似性
(print (equal "hello" "hello")) ; T
(print (equal list-a list-b)) ; T (内容是等效的)
(terpri)
;; EQUALP 更宽松
(print (equalp "hello" "Hello")) ; T (不区分大小写)
(print (equalp 10 10.0)) ; T (忽略数字类型)
)
T
NIL
T
T
NIL
T
T
T
T
  • 默认使用 eql: 当不确定时,从 eql 开始。它是一个安全高效的默认选择。
  • 对数字使用 =: 总是使用 = 进行数值相等性比较,因为它能正确处理不同的数字类型(例如,整数与浮点数)。
  • 对列表和字符串使用 equal: 当你需要比较数据结构的内容时,equal 是你的工具。
  • 避免对数字和列表使用 eq: 对数字或新创建的列表使用 eq 可能会导致意想不到的行为,因为编译器不被要求重用相同的内存对象。
  • 延伸阅读: 如需深入了解,请查阅 Common Lisp Hyperspec,它是该语言的官方标准。