在elisp中替换关联列表中的项目

33

我在Emacs Lisp中有一个列表,如下所示:

(setq a1
 '((:k1 . 1)
   (:k2 . 2)
   (:k3 . 3)))

我想将:k1的值更改为10,类似于(:k1 . 10)。我该怎么做?

我尝试了(setf (assoc :k1 a1) '(:k1 . 10)),但它没有起作用。

6个回答

22

使用alist时,通常在旧值前面添加一个新的cons以“掩盖”旧值,方法如下:

(add-to-list 'a1 '(:k1 10))

执行此操作后,(assoc :k1 a1) 将返回10。

如果您想“撤销”更改以便 assoc 再次返回旧值,请使用此代码:

(setq a1 (delq (assoc :k1 a1) a1))

这将从a1中删除:k1第一个匹配项。


17

从Emacs 25.1开始,alist-get是一个位置表达式,因此您可以这样做:

(setf (alist-get :k1 a1) 10)

8
setf宏并不知道assoc,但您仍然可以手动使用这种方法:
(let ((item (assoc :k1 a1)))
  (setf (car item) :k1)
  (setf (cdr item) 10))

如果只需要为给定的car设置cdr(而不是替换两者),那么我们可以简化为:

(setf (cdr (assoc :k1 a1)) 10)

可能没有必要将车设置为:k1,因为那已经是该值了。一般来说,向关联列表推送新值比修改它们更好,但这完全取决于为什么要设置新值。 - Vatine
1
同意,但是我正在调整问题中的代码,并且那确实设置了车辆,所以我也在这里包含了它。当然,如果不需要设置汽车,则可以简化代码,因此我会添加另一种选择。 - phils

7
这是一个更短、更简单的版本,适用于我的Emacs Lisp(MAC Lisp):

这个版本对我来说很有效。

(setcdr (assoc :k1 a1) 10)

如果您恰好使用的是Common Lisp:

(rplacd (assoc :k1 a1) 10)

(请注意,setcdr和rplacd在返回的值上可能会有略微不同的行为。)

1
这是一个基于gavenkoa建议的函数 -
(defun alist-set (alist-symbol key value)
  "Set KEY to VALUE in alist ALIST-SYMBOL."
  (set alist-symbol
        (cons (list key value) 
              (assq-delete-all key (eval alist-symbol)))))

使用方法 -

(let ((foo '((a 1) (b 2))))
  (alist-set 'foo 'a 3)
  foo) ; => ((a 3) (b 2))

1
关于assq-delete-all:
(setq sql-product-alist
      (cons '(ms-tsql :server ....)
            (assq-delete-all 'ms-tsql sql-product-alist)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接