考虑到键添加到关联列表的频率auto-mode-alist
,我认为有一些习惯方法用于维护具有唯一键的关联列表,但尚未遇到它.
假设我执行以下操作:
(setq alist '())
(add-to-list 'alist '(a . 1))
(add-to-list 'alist '(a . 2))
(add-to-list 'alist '(b . 3))
Run Code Online (Sandbox Code Playgroud)
运行之后,alist包含((b . 3) (a . 2) (a . 1))
.我看到add-to-list
可以选择compare-fn
,所以我认为有一些方法我可以传递得到((b . 3) (a . 1))
结果.我也知道我可以使用哈希表,但很奇怪如何用关联列表惯用它.
没有要求关联列表具有唯一键,如您的示例所示,也没有任何特殊理由期望它们具有唯一键 - 在基础上,它只是列表列表car
,对嵌套列表的s 没有限制.
我认为通过将新对推送到列表前面来覆盖初始键/值对的键没有限制这一事实是不恰当的.alists的一些核心功能隐含地沿着这些方向工作.例如,这里是docstring 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.
Run Code Online (Sandbox Code Playgroud)
因此,它只返回第一个元素,而不管列表后面有多少其他元素具有相同的键.这可能是一个非常有用的功能.
更新. 如果你真的想要防止add-to-list
对先前的键/值对进行阴影处理(尽管你这样做会对语言有所帮助),你可以定义以下函数并将其传递给compare-fn
参数add-to-list
(注意它没有错误 -检查):
(defun key-used-p (elt1 elt2)
"Helper function for add-to-list: returns non-nil if key is
already in use in an association list."
(eq (car elt1) (car elt2)))
(setq alist '((a . 1) (b . 1) (c . 1))) ; original list
(add-to-list 'alist '(a . 2) nil #'key-used-p) ; adds nothing because a in use
(add-to-list 'alist '(d . 2) nil #'key-used-p) ; adds (d . 2)
Run Code Online (Sandbox Code Playgroud)
不要担心具有重复键的项目的alist.通常当您使用alist时,您可以访问带有的项目assoc
,这将返回列表中的第一个匹配项目并忽略其余项目.因此,对待alist的惯用方法是,每次要使用旧键替换alist中的项时,只需添加一个新的点对并忽略旧的.所以无论你有多少重复,assoc
都会忽略它们.您可以通过alist API工作并忽略实现细节,可以这么说.Alist的结构列表是低级别且不相关的.
许多有用的Common Lisp函数都是通过CL库实现的,前缀为cl-
.一个这样的功能是cl-remove-duplicates
,由于关键字参数,它非常通用.(如果你想要一个Common Lisp解决方案,只需使用remove-duplicates
).因此,如果由于某种原因您想要一个具有唯一键的alist,请删除所有重复的项目,但新添加的:
(cl-remove-duplicates my-list
:key #'car
:from-end t)
Run Code Online (Sandbox Code Playgroud)
提供car
密钥等同于编写自定义测试功能,仅比较每2个元素的汽车::test (lambda (a b) (equal (car a) (car b))
.它为什么有效?cl-remove-duplicates
已经使用eql
它作为它的测试函数,正如它在手册中所说,一个关键函数就像一个过滤器,通过该过滤器可以看到元素的功能,因此它不会比较元素本身,而是首先通过关键函数放置元素.
每次看起来方便和优雅时都应该使用CL库,因为它与Emacs一起提供,但我们假设您不能出于某种原因使用它.然后是dash.el
第三方列表操作库.它有一个功能-distinct
,可以删除列表中的多个匹配项.但是它逐字逐句地使用列表的元素,它不理解列表!或者是吗?您可以通过将其分配给-compare-fn
变量来提供自定义比较功能,并-distinct
使用它而不是直接比较它equal
.只是暂时提供一个按键比较的功能let
:
(let ((-compare-fn (lambda (a b)
(equal (car a) (car b)))))
(-distinct my-list))
Run Code Online (Sandbox Code Playgroud)