5.7 将列表用作集合

列表可以表示数学意义上的无序集合 — 只要某个值出现在列表中,就视其为集合的元素,而忽略列表中的顺序。 求两个集合的并集可以使用 append (只要你不介意结果中出现重复元素)。 可以使用 delete-dupsseq-uniq 移除通过 equal 判断的重复元素。其他适用于集合的常用函数包括 memqdelq,以及基于 equal 比较的版本 memberdelete

Common Lisp 说明: Common Lisp 提供用于集合操作的 union(自动去重)和 intersection 函数。在 Emacs Lisp 中,这些功能的变体由 cl-lib 库提供。See Lists as Sets in Common Lisp Extensions

Function: memq object list

该函数检测 object 是否为 list 的成员。如果是,memq 返回从 object 第一次出现位置开始的子列表。否则返回 nilmemq 中的字母 ‘q’ 表示它使用 eq 比较 object 与列表元素。示例:

(memq 'b '(a b c b a))
     ⇒ (b c b a)
(memq '(2) '((1) (2)))    ; The two (2)s need not be eq.Unspecified; might be nil or ((2)).
Function: delq object list

该函数破坏性地从 list 中删除所有与 object 满足 eq 的元素,并返回结果列表。delq 中的字母 ‘q’ 表示它使用 eq 比较元素,与 memqremq 一致。

通常调用 delq 时,你应当使用其返回值,并赋值给保存原列表的变量,原因见下文说明。

delq 删除列表头部元素时,只是向后遍历列表并返回从后续位置开始的子列表。例如:

(delq 'a '(a b c)) ≡ (cdr '(a b c))

当待删元素出现在列表中间时,删除操作会修改 CDR(see 修改列表的 CDR)。

(setq sample-list (list 'a 'b 'c '(4)))
     ⇒ (a b c (4))
(delq 'a sample-list)
     ⇒ (b c (4))
sample-list
     ⇒ (a b c (4))
(delq 'c sample-list)
     ⇒ (a b (4))
sample-list
     ⇒ (a b (4))

请注意,(delq 'c sample-list) 会修改 sample-list,将其中第三个元素移除(拼接剔除);但 (delq 'a sample-list) 并不会剔除任何元素 —— 它仅返回一个更短的列表。切勿想当然地认为:原本存储参数 list 的变量,其元素数量会减少,或是该变量仍指向原始列表!正确的做法是:保存 delq 的返回结果,并使用这个结果。最常见的用法是将返回结果重新存回原本存储原始列表的那个变量中:

(setq flowers (delq 'rose flowers))

下面例子中,delq 试图匹配的 (list 4)sample-list 里的 (4) 满足 equal 但不满足 eq

(delq (list 4) sample-list)
     ⇒ (a c (4))

如果你想删除满足 equal 的元素,请使用下面的 delete

Function: remq object list

该函数返回 list 的副本,并移除所有与 object 满足 eq 的元素。remq 中的字母 ‘q’ 表示它使用 eq 比较对象与列表 list 中的元素。

(setq sample-list (list 'a 'b 'c 'a 'b 'c))
     ⇒ (a b c a b c)
(remq 'a sample-list)
     ⇒ (b c b c)
sample-list
     ⇒ (a b c a b c)
Function: memql object list

该函数 memql 用于检测 object 是否为列表 list 的成员,其通过 eql 函数比较列表成员与 object,因此浮点型元素会按值进行比较。若 object 是列表成员,memql 会返回从该元素在列表 list 中首次出现位置开始的子列表;否则返回 nil

对比 memq 函数的行为:

(memql 1.2 '(1.1 1.2 1.3))  ; 1.2 and 1.2 are eql.
     ⇒ (1.2 1.3)
(memq 1.2 '(1.1 1.2 1.3))  ; The two 1.2s need not be eq.Unspecified; might be nil or (1.2 1.3).

以下三个函数的行为分别类似于 memqdelqremq,但它们使用 equal 而非 eq 来比较元素。See 相等性谓词

Function: member object list

该函数 member 用于检测 object 是否为列表 list 的成员,其通过 equal 函数比较列表成员与 object。若 object 是列表成员,member 会返回从该元素在列表 list 中首次出现位置开始的子列表;否则返回 nil

对比 memq 函数的行为:

(member '(2) '((1) (2)))  ; (2) and (2) are equal.
     ⇒ ((2))
(memq '(2) '((1) (2)))    ; The two (2)s need not be eq.Unspecified; might be nil or (2).
;; Two strings with the same contents are equal.
(member "foo" '("foo" "bar"))
     ⇒ ("foo" "bar")
Function: delete object sequence

该函数从序列 sequence 中移除所有与 object 满足 equal 相等判定的元素,并返回处理后的序列。

sequence 是列表,deletedelq 的关系,等同于 membermemq 的关系:它会像 member 一样,使用 equal 来比较元素与 object;当找到匹配的元素时,它会如同 delq 那样将该元素从列表中剔除。与 delq 相同,你通常应将返回值赋值给原本存储原始列表的变量,以此来使用该返回值。

sequence 是向量或字符串,delete 会返回 sequence 的一个副本,其中所有与 object 满足 equal 相等判定的元素均被移除。

示例:

(setq l (list '(2) '(1) '(2)))   ; 定义变量l为包含三个列表的列表
(delete '(2) l)                  ; 从列表l中删除元素 '(2)
     ⇒ ((1))                     ; 返回值为 ((1))
l
     ⇒ ((2) (1))                 ; 变量l的值变为 ((2) (1))
;; 若想可靠地修改变量l的值,
;; 应写成 (setq l (delete '(2) l))
(setq l (list '(2) '(1) '(2)))   ; 重新定义变量 l 为初始列表
(delete '(1) l)                  ; 从列表 l 中删除元素 '(1)
     ⇒ ((2) (2))                 ; 返回值为 ((2) (2))
l
     ⇒ ((2) (2))                 ; 变量 l 的值变为 ((2) (2))
;; 此场景下,是否重新赋值l没有区别,
;; 但为了兼容其他场景,仍建议显式赋值。
(delete '(2) [(2) (1) (2)])      ; 从向量中删除元素 '(2)
     ⇒ [(1)]                     ; 返回值为 [(1)]
Function: remove object sequence

该函数是 delete 的非破坏性版本,返回序列 sequence (列表、向量或字符串)的副本,其中所有与 object equal 相等的元素均被移除。示例:

(remove '(2) '((2) (1) (2)))
     ⇒ ((1))
(remove '(2) [(2) (1) (2)])
     ⇒ [(1)]

Common Lisp 说明: GNU Emacs Lisp 中的 memberdeleteremove 函数源自 Maclisp,而非 Common Lisp;Common Lisp 版本的这些函数不会使用 equal 比较元素。

Function: member-ignore-case object list

该函数的行为与 member 类似,但要求 object 是字符串,且比较时忽略字母大小写和文本编码形式的差异:大写与小写字母被视为相等,单字节字符串会先转换为多字节字符串再进行比较。

Function: delete-dups list

该函数会破坏性地从 list 中移除所有 equal 相等的重复元素,将结果存入 list 并返回它。 对于 list 中多个 equal 相等的同一元素,delete-dups 会保留第一个出现的元素。非破坏性的去重操作请参见 seq-uniq(see 序列)。

另可参考 “修改列表变量” 章节中的 add-to-list 函数,该函数可向存储在变量中、用作集合的列表添加元素。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike