LISP - 自定义谓词
现代 Lisp:定义自定义谓词
Section titled “现代 Lisp:定义自定义谓词”一个谓词(predicate)是一个函数,它回答关于其输入的一个真/假问题。在 Common Lisp 中,谓词是基础性的。它们返回 T(表示真)或 NIL(表示假)。定义你自己的清晰、健壮的谓词是编写富有表达力且可读性强的 Lisp 代码的关键。
定义谓词的基础知识
Section titled “定义谓词的基础知识”你像定义其他任何函数一样定义谓词,使用 defun。按照惯例,谓词函数的名称以 -P 结尾。
(defun <predicate-name>-p (parameter-list) "可选的文档字符串。" (计算结果为 T 或 NIL 的函数体))示例 1:检查正数
Section titled “示例 1:检查正数”一个简单的谓词,检查数字是否大于零。
(defun positive-p (number) "如果 NUMBER 是一个大于零的数字,则返回 T;否则返回 NIL。" (and (numberp number) (> number 0)))
;; --- 测试 ---(print (positive-p 10)) ;=> T(print (positive-p -5)) ;=> NIL(print (positive-p 0)) ;=> NIL(print (positive-p "abc")) ;=> NIL (因为进行了 `numberp` 检查)注意:我们添加了 (numberp number) 以使函数更健壮。现在它能优雅地处理非数字输入,而不是引发错误。
示例 2:检查空列表
Section titled “示例 2:检查空列表”虽然 Lisp 有内置的 null 函数用于此目的,但自己定义它是一个很好的练习。
(defun empty-list-p (list) "如果 LIST 是空列表 '(),则返回 T;否则返回 NIL。" (null list))
;; --- 测试 ---(print (empty-list-p '())) ;=> T(print (empty-list-p nil)) ;=> T (NIL 等同于 '())(print (empty-list-p '(a b))) ;=> NIL示例 3:检查数字是否在范围内
Section titled “示例 3:检查数字是否在范围内”Common Lisp 的比较运算符如 < 和 <= 可以接受多个参数,这使得范围检查的代码非常清晰和可读。
(defun in-range-p (num lower upper) "如果 NUM 介于 LOWER 和 UPPER 之间(包含边界),则返回 T。" (and (numberp num) (numberp lower) (numberp upper) (<= lower num upper)))
;; --- 测试 ---(print (in-range-p 5 1 10)) ;=> T(print (in-range-p 10 1 10));=> T(print (in-range-p 0 1 10)) ;=> NIL使用类型检查增强谓词
Section titled “使用类型检查增强谓词”为了编写真正健壮的代码,特别是对于库或团队项目,最好在无效输入时发出错误信号,而不是返回 NIL。check-type 宏非常适合此目的。
(defun positive-p (number) "如果 NUMBER 是一个大于零的数字,则返回 T。 如果 NUMBER 不是数字,则发出类型错误信号。" (check-type number number) (> number 0))
;; (positive-p 10) ;=> T;; (positive-p "abc") ;=> 发出一个可纠正的 TYPE-ERROR测试你的谓词
Section titled “测试你的谓词”为你的函数编写测试是一个好习惯。assert 宏是创建基本测试套件的简单方法。如果其参数表达式计算结果为 NIL,它将发出错误信号。
(defun test-predicates () "为我们的自定义谓词运行一个简单的测试套件。" ;; 测试 positive-p (assert (positive-p 5)) (assert (not (positive-p -5))) (assert (not (positive-p 0)))
;; 测试 in-range-p (assert (in-range-p 5 0 10)) (assert (not (in-range-p 11 0 10)))
(format t "所有测试通过!"))
;; 要运行测试:;; (test-predicates)编写谓词的最佳实践
Section titled “编写谓词的最佳实践”- 命名约定:始终使用
-p后缀。这使得代码具有自文档性(例如,(if (user-active-p user) ...))。 - 文档:编写清晰的文档字符串,解释谓词检查什么、其参数以及它返回什么。
- 明确性:确保你的函数始终返回
T或NIL。使用(not ...)来反转条件,而不是依赖其他“假”值。 - 利用内置函数:在编写自定义谓词之前,检查是否有内置的 Lisp 函数已经实现了你需要的功能(例如
evenp、oddp、streamp、listp、null)。 - 考虑输入类型:决定你的谓词是应该对无效类型返回
NIL(灵活),还是使用check-type发出错误信号(健壮)。