如何确保关联列表在Emacs中维护唯一键

时间:2014-08-03 01:36:16

标签: emacs elisp

考虑到密钥被添加到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))作为结果。我也知道我可以使用哈希表,但很奇怪如何使用关联列表以惯用方式进行。

2 个答案:

答案 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))