跨属性列表映射功能?


17

问:跨属性列表映射函数的惯用方式是什么?

各种映射函数(mapcar和族)在诸如列表的序列上映射函数。在处理属性列表时,即试图在列表中包含的每个属性之间进行映射(从第一个元素开始的所有其他元素)时,如何使用这些功能?在我看来,映射功能将需要成对访问元素而不是单个元素的列表。

作为一个玩具示例,如何获取属性列表并收集所有属性值?如果它是一个关联列表,它将非常简单:

(mapcar #'cadr '((:prop1 a) (:prop2 b) (:prop3 c))) ;=> (a b c)

我敢肯定这可以通过循环来完成,但是这似乎有点费力,我想知道是否还有一种更惯用的方式来做到这一点。


请说明您是要仅映射属性值(听起来像是列表mapcar示例的作用)还是要映射成对的属性符号和属性值。我想,后者更通用(更通用)。
画了

@德鲁:我对一般情况更感兴趣;这个例子是我能想到的最简单的例子。我想知道如何在属性/值对上进行映射。如果答案是“循环”,那就这样吧,但我想知道是否有更优雅的解决方案。

示例中的内容不是属性列表。属性列表具有偶数个元素,奇数个元素是属性名称,偶数个元素是属性值。您所拥有的可能称为树(列表列表)。
wvxvw 2015年

Answers:


8

您可能会得到各种loop答案和迭代答案。AFAIK,没有惯用的方法可以做到这一点。我什至不认为只累加属性值(而不是将它们与属性相关联)是很普遍的。

这是一种简单的方法:

(defun prop-values (plist)
  "..."
  (let ((pl    (cdr plist))
        (vals  ()))
    (while pl
      (push (car pl) vals)
      (setq pl  (cddr pl)))
    (nreverse vals)))

对于更一般的情况,要在plist上映射二进制函数:

(defun map-plist (fn plist)
  "..."
  (let ((pl    plist)
        (vals  ()))
    (while pl
      (push (funcall fn (car pl) (cadr pl)) vals)
      (setq pl (cddr pl)))
    (nreverse vals)))

(setq foo '(a 1 b 2 c 3 d 4 e 5))

(map-plist #'cons foo) ; => ((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))

13

这可能取决于情况。通常,如果需要将多个值与多个名称绑定在一起,则可以使用哈希表,但是如果必须使用属性列表,则可以使用cl-loop。以下是一些示例:

(cl-loop for (key value) on '(:prop1 a :prop2 b :prop3 c) by 'cddr
         collect value)
;;; (a b c)

如果您有数据结构,请在示例中显示:

(cl-loop for (key value) in '((:prop1 a) (:prop2 b) (:prop3 c))
         collect value)
;;; (a b c)

5

对我来说,答案是将plist变成alist,这是等效的数据结构,深度只有一半,更易于操作。


3
好吧,这是一个答案(也是很好的建议),但更多是评论。
德鲁

4

使用当前Git HEAD(将成为Emacs 25)中的Emacs,您可以执行以下操作,即使用新seq-partition功能轻松地将plist转换为alist ,然后使用standard处理alist条目 mapcar

(let* ((my-plist (list :a 1 :b 2 :c 3 :more (list 4 5 6)))
       (my-alist (seq-partition my-plist 2))
       (my-reverse-alist (mapcar (lambda (entry)
                                   (let ((prop (car entry))
                                         (val  (cadr entry)))
                                     (list val prop)))
                                 my-alist)))
  (message "my-plist: %s\nmy-alist: %s\nmy-reverse-alist: %s"
           my-plist my-alist my-reverse-alist))
;; my-plist: (:a 1 :b 2 :c 3 :more (4 5 6))
;; my-alist: ((:a 1) (:b 2) (:c 3) (:more (4 5 6)))
;; my-reverse-alist: ((1 :a) (2 :b) (3 :c) ((4 5 6) :more))

看看seq-partition使用的文档C-h f seq-partition RET


1

一种想法是使用-map-indexedfrom dash并将转换仅应用于列表的奇数:

(-non-nil (-map-indexed (lambda (index item) (when (oddp index) item))
  '(a x b y c z))) ; => (x y z)
(-non-nil (--map-indexed (when (oddp it-index) it) '(a x b y c z))) ; => (x y z)

另一个想法是只使用ht<-plistfrom 将plist转换为哈希表ht

(ht-values (ht<-plist '(a x b y c z) 'equal)) ; (z y x)

请注意,哈希表不会保留项目的顺序。

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.