【问题标题】:Elisp: How to delete an element from an association list with string keyElisp:如何使用字符串键从关联列表中删除元素
【发布时间】:2012-04-06 10:20:12
【问题描述】:

现在可以正常工作了:

(setq al '((a . "1") (b . "2")))
(assq-delete-all 'a al)

但我在我的应用程序中使用字符串作为键:

(setq al '(("a" . "foo") ("b" . "bar")))

这没有做任何事情:

(assq-delete-all "a" al)

我认为这是因为字符串对象实例不同(?)

那么我应该如何从关联列表中删除带有字符串键的元素呢?或者我应该放弃并使用符号作为键,并在需要时将它们转换为字符串?

【问题讨论】:

  • 顺便说一句,你需要将assq-delete-all 的结果分配回变量,即使它是破坏性操作:(setq al (assq-delete-all 'a al))。如果列表为空怎么办? al 必须取值 nil:这将如何发生?或者,如果您删除第一个元素,即 al 最初指向的头部缺点怎么办? al 必须更新才能跳到第二个单元格。
  • 为了完整起见,要按值(和/或键)删除,您也可以使用seq-removecl-remove(-if)。这仅返回“过滤”列表,因此要更新值,您应该使用 setq 更新原始值(始终推荐/更安全,在使用像 delete 这样的“破坏性”操作时也是如此;至少是这样的根据 Paul Graham 所说,在 Common-Lisp 中;他在一本书或多本书中提到了这一点)。

标签: emacs lisp elisp


【解决方案1】:

Emacs 27+ 包括assoc-delete-all,它适用于字符串键,也可以与任意测试函数一起使用。

(assoc-delete-all KEY ALIST &optional TEST)

Delete from ALIST all elements whose car is KEY.
Compare keys with TEST.  Defaults to ‘equal’.
Return the modified alist.
Elements of ALIST that are not conses are ignored.

例如:

(setf ALIST (assoc-delete-all KEY ALIST))

在早期版本的 Emacs 中,cl-delete 提供了一个替代方案:

(setf ALIST (cl-delete KEY ALIST :key #'car :test #'equal))

这相当于从 ALIST 中删除列表项的 carequal 到 KEY 的项目。

n.b. Kaz 的答案已经提到了后一个选项,但是使用较旧的 (require 'cl) 名称 delete*remove*,而您现在(为了支持 Emacs 24+)使用 cl-deletecl-remove(它们是自动的) -加载)。

【讨论】:

    【解决方案2】:

    如果使用 emacs 25 或更新版本,您可以使用 alist-get

    (setf (alist-get "a" al t t 'equal) t)

    【讨论】:

    • 正确,但 很难 阅读/解释 IMO,我几乎觉得这是一个错误功能。我只能假设它在那里支持您可能会或可能不会删除值的动态情况。如果我要使用它,我可能最终会写成(setf (alist-get "a" al :remove :remove 'equal) :remove)——但我很确定我永远不会将它用于此目的。
    【解决方案3】:

    如果您知道列表中只能有一个匹配条目,您也可以使用以下表格:

    (setq al (delq (assoc <string> al) al)
    

    请注意,setq(您的示例代码中缺少该符号)对于列表上的“删除”操作非常重要,否则当删除的元素恰好是列表中的第一个时操作会失败。

    【讨论】:

      【解决方案4】:

      assq 中的 q 传统上意味着 eq 相等用于对象。

      换句话说,assqeq 风格的 assoc

      字符串不遵循eq 等式。两个等价字符序列的字符串可能不是eq。 Emacs Lisp 中的assoc 使用equal 相等,它适用于字符串。

      所以这里需要一个assoc-delete-all 用于基于equal 的关联列表,但该功能不存在。

      当我搜索assoc-delete-all 时,我能找到的只有这个邮件列表线程: http://lists.gnu.org/archive/html/emacs-devel/2005-07/msg00169.html

      滚动你自己的。这相当简单:您沿着列表前进,并将所有这些条目收集到一个新列表中,其carequal 下的给定键不匹配。

      一个有用的东西可能是 Common Lisp 兼容性库。 http://www.gnu.org/software/emacs/manual/html_node/cl/index.html

      那里有一些有用的函数,例如remove*,您可以使用它们从列表中删除,使用自定义谓词函数来测试元素。有了它,您可以执行以下操作:

      ;; remove "a" from al, using equal as the test, applied to the car of each element
      (setq al (remove* "a" al :test 'equal :key 'car))
      

      破坏性变体是delete*

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-05
        • 2018-10-21
        • 1970-01-01
        相关资源
        最近更新 更多