Skip to content

LISP - 谓词

在 Common Lisp 中,谓词(predicate)是一种用于测试特定条件的函数。它返回一个真或假的值。Lisp 中一个关键概念是其真值的表示:符号 nil 是唯一的假值,而任何其他值都被认为是真值。按照惯例,符号 t 被用作规范的真值。

谓词函数通常以 -p 或 p 作为后缀命名(‘p’ 代表 ‘predicate’),例如 numberp 或 evenp。

以下是一些最常用的谓词,按其用途分组。

在 Lisp 中,选择正确的相等谓词是一项常见任务。它们在比较其参数的严格程度上有所不同。

谓词描述
eq如果参数在内存中是完全相同的对象,则返回 t。这是最严格、最快的比较。
eql与 eq 类似,但对于相同的数字和字符也返回 t。例如,(eql 5 5) 为 t,但 (eql 5 5.0) 为 nil。
equal如果参数在结构上相似,则返回 t。它递归地比较列表和字符串。例如,两个元素相同的不同列表是 equal 的。
equalp最宽松的比较。与 equal 类似,但忽略字符串的大小写,并比较不同类型的数字(例如,(equalp 5 5.0) 为 t)。
谓词描述
atom如果参数是原子(即,不是 cons cell/列表),则返回 t。
listp如果参数是列表(cons cell 或 nil),则返回 t。
numberp如果参数是任何类型的数字,则返回 t。
integerp如果参数是整数,则返回 t。
floatp如果参数是浮点数,则返回 t。
stringp如果参数是字符串,则返回 t。
symbolp如果参数是符号,则返回 t。
null如果参数是 nil,则返回 t,否则返回 nil。
谓词描述
zerop接受一个数值参数,如果为零则返回 t。
evenp接受一个整数参数,如果为偶数则返回 t。
oddp接受一个整数参数,如果为奇数则返回 t。
>接受一个或多个数字。如果它们严格递减,则返回 t。
<接受一个或多个数字。如果它们严格递增,则返回 t。
=接受一个或多个数字。如果它们在数值上全部相等,则返回 t。

让我们创建一个源文件 main.lisp 来演示这些概念。此示例将突出相等谓词之间的差异,并展示一些常见的类型检查。

;;; main.lisp - 演示 Common Lisp 谓词
(defun run-predicate-demos ()
"运行一系列谓词函数演示。"
(format t "--- 相等谓词演示 ---~%")
(let ((list1 (list 'a 'b))
(list2 (list 'a 'b)))
(format t "(eq list1 list2) -> ~a (内存中的不同对象)~%" (eq list1 list2))
(format t "(equal list1 list2) -> ~a (结构上等价)~%~%" (equal list1 list2)))
(format t "--- 数值谓词演示 ---~%")
(format t "(oddp 7) -> ~a~%" (oddp 7))
(format t "(evenp 10) -> ~a~%" (evenp 10))
(format t "(zerop 0.0) -> ~a~%" (zerop 0.0))
(format t "(= 5 5.0) -> ~a (数值相等)~%" (= 5 5.0))
(format t "(> 10 5 1) -> ~a~%" (> 10 5 1))
(format t "(< 2 4 8) -> ~a~%~%" (< 2 4 8)))
(format t "--- 类型谓词演示 ---~%")
(format t "(listp '(1 2 3)) -> ~a~%" (listp '(1 2 3)))
(format t "(atom 'my-symbol) -> ~a~%" (atom 'my-symbol))
(format t "(stringp \"Hello\") -> ~a~%" (stringp "Hello"))
(format t "(null nil) -> ~a~%" (null nil))))
;; 要运行此代码,请在 Lisp 环境中加载文件并调用 (run-predicate-demos)
(run-predicate-demos)

当您在 Common Lisp 实现(如 SBCL)中加载并执行此代码时,您将看到:

--- 相等谓词演示 ---
(eq list1 list2) -> NIL (内存中的不同对象)
(equal list1 list2) -> T (结构上等价)
--- 数值谓词演示 ---
(oddp 7) -> T
(evenp 10) -> T
(zerop 0.0) -> T
(= 5 5.0) -> T (数值相等)
(> 10 5 1) -> T
(< 2 4 8) -> T
--- 类型谓词演示 ---
(listp '(1 2 3)) -> T
(atom 'my-symbol) -> T
(stringp "Hello") -> T
(null nil) -> T

谓词是控制程序流程的基础。这是一个经典的递归阶乘函数,它使用 zerop 谓词作为其基本情况。

;;; factorial.lisp - 递归阶乘实现
(defun factorial (n)
"计算非负整数 N 的阶乘。"
(check-type n (integer 0 *) "N 必须是非负整数。")
(if (zerop n)
1 ; 基本情况:0! = 1
(* n (factorial (1- n))))) ; 递归步骤:n * (n-1)!
;; 示例用法:
(let ((num 6))
(format t "Factorial of ~d is: ~d~%" num (factorial num)))

执行此代码将产生:

Factorial of 6 is: 720
  • 选择相等比较: eq 用于符号比较,eql 作为一般默认值,equal 用于比较列表或序列,当需要灵活的、不区分大小写或不区分类型的比较时,使用 equalp。
  • 数值比较: 始终使用 = 进行数字比较,而不是 eql 或 equal,因为 = 能正确处理不同的数字类型(例如整数和浮点数)。
  • 错误检查: 使用谓词进行健壮的错误检查。阶乘示例中的 check-type 宏是强制参数具有正确类型的强大方法。