3

我想转换这个列表列表:

(setq terms '((("name" . "t1c1")
               ("taxonomy" . "category"))
              (("name" . "t1c2")
               ("taxonomy" . "category"))
              (("name" . "t1k1")
               ("taxonomy" . "post_tag"))
              (("name" . "t1k2")
               ("taxonomy" . "post_tag"))
              (("name" . "t1k3")
               ("taxonomy" . "post_tag"))))

进入其他列表:

(("category" "t1c1" "t1c2")
 ("post_tag" "t1k1" "t1k2" "t1k3"))

我想出了:

(reduce
 '(lambda (lists term)
    (let* ((name (cdr (assoc "name" term)))
           (taxonomy (cdr (assoc "taxonomy" term)))
           (existing (assoc taxonomy lists)))
      (if existing
          (progn
            (setcdr existing (sort (cons name (cdr existing)) 'string<)))
        (push (list taxonomy name) lists)))
    lists)
 terms
 :initial-value nil)

这看起来很不雅—— let* 和 if 语句的使用看起来都像是潜在的代码异味。

我会很感激在 elisp 中执行此操作的任何更好方法的示例——更好可能意味着更纯粹的功能,更好地使用内置函数来表示某些操作等。

哦,我想对结果列表的内容进行排序——这让我更容易测试输出。

4

2 回答 2

2

想到的最优雅的是使用(loop ...)宏。有人可能会说这还不够简洁,但我认为它的简洁性和表现力胜过纯粹性:

(loop
 with result = '()
 for term in terms
 for name = (aget term "name")
 for taxonomy = (aget term "taxonomy")
 do (aput 'result taxonomy
          (sort (cons name (aget result taxonomy)) 'string<))
 finally return result)
于 2013-03-27T15:47:30.217 回答
1

我会很感激在 elisp 中执行此操作的任何更好方法的示例——更好可能意味着更纯粹的功能,更好地使用内置函数来表示某些操作等。

也许是这样的(假设cl-lib已加载):

(cl-flet ((step (lists term)
            (let ((name (aget term "name"))
                  (taxonomy (aget term "taxonomy")))
              (cl-acons taxonomy
                        (sort (cons name (aget lists taxonomy)) #'string<)
                        (cl-remove taxonomy lists :key #'car :test #'equal)))))
  (reduce #'step dhl-terms :initial-value nil))

它会比您的解决方案效率低,因为“更纯粹的功能”也意味着不使用setcdr- 它会破坏新条目,并再通过一次以删除旧条目。

于 2013-03-27T16:26:09.200 回答