合并两个属性列表的功能?


11

我没有找到标准的Elisp库函数来合并两个属性列表,如下所示:

(setq pl nil)
(setq pl (plist-put pl 'key-1 'value-1))
(setq pl (plist-put pl 'key-2 'value-2))

我可以用构建一些东西dolist,但是在这样做之前,我想检查一下我是否在忽略某些库中的现有函数。

根据评论进行更新

  1. 针对“多种方式”的评论:

我可以想象没有这样的功能,因为对于这个问题可能有不同的(并且可能是有效的)答案:当您重复使用具有不同值的属性名称时该怎么办?

是的,有一个关于如何合并重复项的问题,但是解决此问题的方法相对较少。我看到两种通用方法。首先,参数顺序可以解决重复项;例如,最右边的胜利,如Clojure的merge中。其次,合并可以委托给用户提供的回调函数,就像Ruby的merge一样

无论如何,存在不同方式的事实并不妨碍许多其他语言标准库提供合并功能。关于排序,可以说相同的一般论点,但Elisp提供了排序功能。

  1. “你能详细说明吗?” /“请准确指定您要寻找的行为。”

一般来说,我对Elisp社区使用的内容持开放态度。如果您想要一个特定的示例,那么下面的示例将是可行的:

(a-merge-function '(k1 1) '(k2 2 k3 3) '(k3 0))

并返回

'(k1 1 k2 2 k3 0))

这将是最右边的风格,例如Clojure的合并。

  1. “它们是清单,所以追加?”

不,append不保留属性列表的语义。这个:

(append '(k1 1 k2 2) '(k2 0))

返回此:

(k1 1 k2 2 k2 0)

append是“ C源代码”中的内置函数。

(附加&rest序列)

连接所有参数,并将结果制成列表。结果是一个列表,其元素是所有参数的元素。每个参数可以是列表,向量或字符串。最后一个参数不被复制,仅用作新列表的尾部。

  1. “并且您的示例没有显示任何类似合并的内容-它甚至没有显示两个属性列表。”

是的,它确实; 它会逐步进行合并。它显示了使用Elisp记录在案的属性列表功能进行合并是多么麻烦:

(setq pl nil)
(setq pl (plist-put pl 'key-1 'value-1))
(setq pl (plist-put pl 'key-2 'value-2))

只需显示来自的结果输出值pl

(key-1 value-1 key-2 value-2)

重申一下,我能够编写一个函数来解决此问题,但是我首先想弄清楚这种函数是否存在于常用位置。

最后,如果您因不清楚该问题而对这个问题投了反对票,那么我现在要尽力澄清一下,所以请您重新考虑一下。这不是缺乏研究。“列表”上的Elisp文档无法回答问题。


2
它们是列表,所以append
abo-abo 2014年

2
请准确指定您要寻找的行为。有很多方法可以“合并”两个列表。您的示例没有显示任何类似合并的内容-它甚至没有显示两个属性列表。到目前为止,这个问题尚待解决,尚不清楚。FWIW,请注意,一对更靠近plist的对阴影的任何对都具有相同的密钥,而该对密钥的密钥对离对更远。因此,合并可能意味着把从一个plist中元素的元素之前,从其他等等
德鲁

1
@ abo-abo:事实证明,《Emacs Lisp手册》明确指出属性名称必须不同
君士坦丁

3
为了使最右边的列表中赢了,你只需要扭转名单你传递的顺序append(let ((args '((:a 1 :b 1) (:b 2) (:a 3)))) (apply #'append (reverse args))) => (:a 3 :b 2 :a 1 :b 1)这是一样的(:a 3 :b 2 :a 1),只要你只使用的plist函数访问的plist。
tarsius

1
@Constantine:对,尽管两人plist-getplist-member似乎并不在乎是否有多个相同的密钥。看起来他们在这方面的行为类似于alists :(plist-get '(:a "a" :b "b" :a "c") :a) ==> "a"。同时,(plist-put '(:a "a" :b "b" :a "c") :a "d")替换第一个:a键的值,而不替换第二个键的值。

Answers:


8

Emacs随附的组织模式具有plist合并功能:

(defun org-combine-plists (&rest plists)
  "Create a single property list from all plists in PLISTS.
The process starts by copying the first list, and then setting properties
from the other lists.  Settings in the last list are the most significant
ones and overrule settings in the other lists."
  (let ((rtn (copy-sequence (pop plists)))
        p v ls)
    (while plists
      (setq ls (pop plists))
      (while ls
        (setq p (pop ls) v (pop ls))
        (setq rtn (plist-put rtn p v))))
    rtn))

要使用它,您需要(require 'org)首先加载文件。不幸的是,这是一个非常大的文件,大小为900 + KB,因此它实际上不能用作实用程序库。像标准plist软件包这样的东西会很不错。

我最近刚开始做一个很小的工作,意识到在列表论点上,列表和plists的处理方式不一样-例如,(plist-get LIST KEY)vs(assoc KEY LIST),这一定是不幸的优化遗物(或?) 。

但是,是的,Emacs确实需要一个不错的plist库-我在搜索中没有遇到过一个plist库,但是仍然有可能在某个地方存在一个plist库,否则我们必须启动一个并将其放在Elpa / Melpa上。

具有相同接口的清单库也很高兴。


6

阅读手册并从中浏览列表C-u C-h a plist RET不会打开合并两个属性列表的任何功能。Common Lisp扩展没有提供专门用于属性列表的任何功能,仅提供了位置(getf/ setf/…)支持。因此,您要么需要依赖第三方库,要么自己滚动库。

自己滚动不是很困难。在冲突的情况下,此实现使用最后的值。

(defun plist-merge (&rest plists)
  (if plists
      (let ((result (copy-sequence (car plists))))
        (while (setq plists (cdr plists))
          (let ((plist (car plists)))
            (while plist
              (setq result (plist-put result (car plist) (car (cdr plist)))
                    plist (cdr (cdr plist))))))
        result)
    nil))

(plist-merge '(:x 2 :y 3)
             '(     :y 0 :z 7))
=>            (:x 2 :y 0 :z 7)

很好 为什么您copy-sequence是第一个列表,而不是其他列表?而且,您可以使用cadr和清除嵌套cddr
fommil

实际上,org-combine-plists(下面)或多或少是清理后的版本。我还是不明白他们为什么要copy-sequence开车。
fommil

0

我知道已经解决了这个问题,但是如果有人感兴趣,我会采用该org实现并在其中打了一些代码

(defun plist-merge (&rest plists)
  "Create a single property list from all PLISTS.
Inspired by `org-combine-plists'."
  (let ((rtn (pop plists)))
    (dolist (plist plists rtn)
      (setq rtn (plist-put rtn
                           (pop plist)
                           (pop plist))))))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.