Skip to content

LISP - 元素位置

Common Lisp 提供了多功能的 position 函数,用于查找序列(如列表或向量)中元素的索引。序列的第一个索引是 0。

(position item sequence &key :from-end :test :key :start :end)
  • item:要搜索的元素。
  • sequence:要在其中搜索的列表、向量或字符串。
  • 返回值:找到的第一个匹配元素的零基索引。如果未找到该项,则返回 NIL。
;; 查找符号 'c 的位置
(position 'c '(a b c d e c)) ; -> 2
;; 搜索一个不存在的项
(position 'z '(a b c d e)) ; -> NIL
;; 向量的语法是 #( ... )
(position #\d #(#\a #\b #\c #\d)) ; -> 3

position 函数的强大之处在于它的可选关键字参数。

  • :from-end t:从序列的末尾而不是开头开始搜索。
  • :start index:从指定的 index 开始搜索。
  • :end index:在指定的 index 之前停止搜索。
  • :test function:一个用于比较项与序列元素的函数。这对于非默认的相等性检查至关重要。
  • :key function:在比较发生之前应用于序列每个元素的函数。
;; 查找 'c 的最后一个出现位置
(position 'c '(a b c d c f) :from-end t) ; -> 4
;; 从索引 3 开始查找 'c'
(position 'c '(a b c d c f) :start 3) ; -> 4

初学者常犯的一个错误是未能理解 Lisp 中不同的相等性函数。position 的默认 :test 是 #'eql,这在比较字符串或列表时可能不是你想要的结果。

  • eq:比较内存地址。仅当是完全相同的对象时才为真。
  • eql(默认):类似于 eq,但对于相同的字符和相同类型的数字也为真。
  • equal:递归地比较值。如果字符串具有相同的字符,或者列表具有相同的 equal 元素,则为真。
  • equalp:一个更宽松的 equal。在字符串中忽略大小写,并比较数值而不考虑类型(例如,(equalp 2 2.0) 为真)。
;; 这返回 NIL,因为两个 "foo" 字符串是不同的对象。
(position "foo" '("bar" "foo" "baz")) ; -> NIL
;; 这可行,因为 `equal` 比较的是字符串内容。
(position "foo" '("bar" "foo" "baz") :test #'equal) ; -> 1
;; 查找子列表
(position '(1 2) '((1 1) (1 2) (1 3)) :test #'equal) ; -> 1

:key 参数在复杂数据结构内部进行搜索时非常有用。它允许你指定要查看每个元素的哪个部分。

示例:搜索属性列表 (plists) 的列表

Section titled “示例:搜索属性列表 (plists) 的列表”
(defvar *people*
'((:name "Alice" :age 30)
(:name "Bob" :age 25)
(:name "Charlie" :age 35)))
;; 查找名为 "Bob" 的人的位置。
;; :key 在比较前从每个人的属性列表 (plist) 中提取名称。
(position "Bob" *people*
:key #'(lambda (person) (getf person :name))
:test #'string-equal) ; string-equal 是一个不区分大小写的相等比较函数
1