Skip to content

LISP - `every` 函数

every 函数是 Common Lisp 中一个强大的高阶函数(higher-order function),用于测试给定**谓词(predicate)**是否对一个或多个序列的每个元素都为真。它是进行数据验证和属性测试的简洁而富有表现力的工具。

(every predicate sequence &rest more-sequences)
  • predicate(谓词):一个函数(或函数名符号),它接受与所提供序列数量相同的参数。它应该在为真时返回非 nil 值,在为假时返回 nil。
  • sequence(序列):第一个要测试的序列(列表、向量或字符串)。
  • more-sequences(更多序列):可选的额外序列。

如果谓词对所有元素都为真,则返回 T。如果谓词对任何元素返回 nil,every 会立即停止处理并返回 nil。

让我们测试序列中的所有数字是否都是偶数。

;; 在列表上使用内置谓词 'evenp'
(print (every #'evenp '(2 4 6 8 10)))
; => T
;; 在向量上使用 'evenp'
(print (every #'evenp #(2 4 6 8 10)))
; => T
;; 包含奇数的失败示例
(print (every #'evenp '(2 4 5 6 8)))
; => NIL
T
T
NIL

您可以利用 lambda 函数即时定义一个自定义测试。

;; 检查所有数字是否大于 0(正数)
(let ((numbers '(1 5 12 99)))
(print (every #'(lambda (x) (> x 0)) numbers)))
; => T
;; 检查所有字符串的长度是否为 3
(let ((words '("cat" "dog" "ant")))
(print (every #'(lambda (word) (= (length word) 3)) words)))
; => T
;; 失败示例
(let ((mixed-words '("cat" "fish" "ant")))
(print (every #'(lambda (word) (= (length word) 3)) mixed-words)))
; => NIL
T
T
NIL

当您提供多个序列时,谓词在每一步会从每个序列中接收一个元素。迭代在最短的序列耗尽时停止。

;; 检查两个列表中对应元素是否相等
(print (every #'= '(1 2 3) '(1 2 3)))
; => T
;; 相等性检查的失败示例
(print (every #'= '(1 2 3) '(1 5 3)))
; => NIL
;; 检查第一个列表中的每个元素是否小于第二个列表中对应的元素
(print (every #'< '(10 20 30) '(11 22 33)))
; => T
T
NIL
T
  • 数据验证:确保数据集中的所有项都符合特定格式或约束,例如 (every #'valid-email-p user-emails)。
  • 状态检查:在游戏或模拟中,检查所有对象是否共享特定状态,例如 (every #'is-enemy-defeated? active-enemies)。
  • 算法前置条件:在运行复杂算法之前,验证输入数据是否满足要求。
  • 专家提示:every 函数有“兄弟姐妹”!使用 some 来检查谓词是否对至少一个元素为真,使用 notany 来检查谓词是否对所有元素都为假,使用 notevery 来检查谓词是否对至少一个元素为假。
  • 调试技巧:如果 every 意外返回 nil,您可以通过将谓词包装在一个 lambda 中来调试,该 lambda 会打印它正在测试的值,例如 (every #'(lambda (x) (format t "Testing: ~a~%" x) (evenp x)) my-list)。