Skip to content

LISP - 列表谓词

谓词是测试条件并返回布尔值的函数。在 Common Lisp 中,标准的假值为 NIL,任何其他值都被视为真。列表谓词是检查和验证列表结构的重要工具,构成了安全且健壮的列表处理代码的基础。

这三个谓词是检查类列表数据性质的基础。理解它们的区别,特别是关于空列表 NIL 的区别,至关重要。

谓词回答的问题对…返回 T关键行为
listp“这是一个恰当的列表吗?”'(a b c),NIL(空列表)最通用的列表检查器。(listp nil) 返回 T。
consp“这是一个 cons 单元格吗?”'(a b c),'(a . b)检查非空列表结构。(consp nil) 返回 NIL,因为 NIL 不是 cons 单元格。
null“这是空列表吗?”NIL,'()检查空列表 NIL 的标准方法。它是 consp 的逆操作。
(let ((proper-list '(1 2 3))
(dotted-list '(a . b))
(empty-list nil)
(atom 'x))
(format t "~12a | listp: ~a, consp: ~a, null: ~a~%" "Proper List"
(listp proper-list) (consp proper-list) (null proper-list))
(format t "~12a | listp: ~a, consp: ~a, null: ~a~%" "Dotted List"
(listp dotted-list) (consp dotted-list) (null dotted-list))
(format t "~12a | listp: ~a, consp: ~a, null: ~a~%" "Empty List"
(listp empty-list) (consp empty-list) (null empty-list))
(format t "~12a | listp: ~a, consp: ~a, null: ~a~%" "Atom"
(listp atom) (consp atom) (null atom)))
Proper List | listp: T, consp: T, null: NIL
Dotted List | listp: T, consp: T, null: NIL
Empty List | listp: T, consp: NIL, null: T
Atom | listp: NIL, consp: NIL, null: NIL

member 检查列表中是否存在某个元素。如果找到,它将返回从该元素首次出现位置开始的子列表(一个真值)。如果未找到,则返回 NIL。

  • 返回值:请记住,member 返回的是列表的尾部,而不仅仅是 T。
  • 相等性测试:默认情况下,member 使用 #'eql。您可以使用 :test 关键字指定不同的比较函数,例如使用 #'equal 来比较结构。
(let ((my-list '(a b c b d)))
(format t "Is 'b in the list? ~a~%" (member 'b my-list)) ; -> (B C B D)
(format t "Is 'z in the list? ~a~%" (member 'z my-list))) ; -> NIL
(let ((nested-list '((x 1) (y 2) (z 3))))
;; #'eql(默认)无法找到结构相似的列表
(format t "Find (y 2) with #'eql? ~a~%" (member '(y 2) nested-list))
;; #'equal 成功
(format t "Find (y 2) with #'equal? ~a~%" (member '(y 2) nested-list :test #'equal)))
Is 'b in the list? (B C B D)
Is 'z in the list? NIL
Find (y 2) with #'eql? NIL
Find (y 2) with #'equal? ((Y 2) (Z 3))

assoc(关联)用于搜索关联列表(alist),它是一个键值对的列表。如果找到键,它将返回整个键值对。否则,返回 NIL。

(let ((config '((:user . "admin") (:port . 8080) (:debug . t))))
(format t "Port setting: ~a~%" (assoc :port config)) ; -> (:PORT . 8080)
(format t "Host setting: ~a~%" (assoc :host config)) ; -> NIL
;; 要仅获取值,请结合 `cdr` 使用
(let ((port-pair (assoc :port config)))
(if port-pair
(format t "Port value: ~a~%" (cdr port-pair)) ; -> 8080
(format t "Port not found.~%"))))
Port setting: (:PORT . 8080)
Host setting: NIL
Port value: 8080

endp 是一个通用谓词,用于检查列表是否已到达末尾。如果其参数为 NIL,则返回 T,否则返回 NIL。它相对于 null 的主要优势在于,如果给定非列表参数,它会发出错误信号,这使其成为终止递归函数或预期列表的循环的更安全选择。

(defun count-elements (lst)
"使用 `endp` 安全地计数列表中的元素。"
(if (endp lst)
0
(1+ (count-elements (rest lst)))))
(format t "Count for '(a b c): ~a~%" (count-elements '(a b c))) ; -> 3
(format t "Count for nil: ~a~%" (count-elements nil)) ; -> 0
;; (count-elements 'not-a-list) ; 这将正确地发出错误信号。
Count for '(a b c): 3
Count for nil: 0