考虑到密钥被添加到auto-mode-alist
之类的关联列表的频率,我假设有一些习惯方法用于维护具有唯一密钥的关联列表,但尚未遇到它。
假设我执行以下操作:
(setq alist '())
(add-to-list 'alist '(a . 1))
(add-to-list 'alist '(a . 2))
(add-to-list 'alist '(b . 3))
运行之后,alist包含((b . 3) (a . 2) (a . 1))
。我看到add-to-list
可以选择compare-fn
,因此我假设我可以通过一些方法来获取((b . 3) (a . 1))
作为结果。我也知道我可以使用哈希表,但很奇怪如何使用关联列表以惯用方式进行。
答案 0 :(得分:7)
没有要求关联列表具有唯一键,如您的示例所示,也没有任何特殊理由期望它们具有唯一键 - 在基础上,它只是一个列表列表,对{{1嵌套列表的。
我认为通过将新对推送到列表前面来覆盖初始键/值对的键没有限制这一事实是不恰当的。 alists的一些核心功能隐含地沿着这些方向工作。例如,这里是car
的文档字符串:
assoc
因此,它只返回第一个元素,而不管列表后面有多少其他元素具有相同的键。这可能是一个非常有用的功能。
更新。如果你真的想阻止Return non-nil if KEY is `equal' to the car of an element of LIST.
The value is actually the first element of LIST whose car equals KEY.
遮蔽先前的键/值对(尽管你这样做是在对抗语言),你可以定义跟随函数并将其传递给add-to-list
中的compare-fn
参数(请注意,它执行零错误检查):
add-to-list
答案 1 :(得分:6)
不要担心列表中包含重复键的项目。通常,当您使用alist时,可以使用assoc
访问项目,该项目返回列表中的第一个匹配项目并忽略其余项目。因此,对待alist的惯用方法是,每次要使用旧键替换alist中的项时,只需添加一个新的点对并忽略旧的。因此,无论您有多少重复项,assoc
都会忽略它们。您可以通过alist API工作并忽略实现细节,可以这么说。 Alist的结构列表是低级别且不相关的。
许多有用的Common Lisp函数都是通过CL library实现的,前缀为cl-
。一个这样的函数是cl-remove-duplicates
,由于关键字参数,它非常通用。 (如果您需要Common Lisp解决方案,只需使用remove-duplicates
)。因此,如果由于某种原因您需要具有唯一键的alist,请删除所有重复的项目,但新添加的项目为:
(cl-remove-duplicates my-list
:key #'car
:from-end t)
提供car
作为键相当于编写一个自定义测试函数,该函数仅比较每2个元素的汽车::test (lambda (a b) (equal (car a) (car b))
。它为什么有效? cl-remove-duplicates
已经使用eql
作为其测试函数,正如manual中所述,关键函数就像一个过滤器,通过该过滤器可以看到函数的元素,因此它不会比较元素本身,但首先通过关键函数放置元素。
每次看起来方便和优雅时都应该使用CL库,因为它是随Emacs一起提供的,但我们假设您出于某些原因无法使用它。然后是dash.el
第三方列表操作库。它有一个函数-distinct
,用于删除列表中的多个匹配项。但是它逐字逐句地使用列表的元素,它不理解列表!或者是吗?您可以通过将自定义比较功能分配给-compare-fn
变量来提供自定义比较功能,-distinct
将使用它而不是直接将其与equal
进行比较。只是暂时提供一个按键与let
进行比较的功能:
(let ((-compare-fn (lambda (a b)
(equal (car a) (car b)))))
(-distinct my-list))