【问题标题】:Common Lisp - Is there a built-in function to filter a plist by keys?Common Lisp - 是否有内置函数可以按键过滤 plist?
【发布时间】:2017-11-26 15:25:32
【问题描述】:

我正在寻找像下面的pfilter-by-keys 这样的内置函数:

(pfilter-by-keys '(:a :b) '(:c 10 :b 20 :a 4))
;; => (:B 20 :A 4)

它的代码很简单:

(defun pfilter-by-keys (keys plist)
  "List -> PList -> PList
  Returns a new plist with only the keys/values correspondent to the given
  keys."
  (loop for (k v) on plist by #'cddr
        when (member k keys :test #'equal)
          append (list k v)))

CL 有没有像上面那样的内置函数?

PS.:Alexandria 有一个非常接近的功能:remove-from-plist

【问题讨论】:

  • 在你的函数中,如果你的键是符号,你可能应该通过eqeql 而不是equal 进行测试。或者至少有一个默认为eql:test 参数。您还可以将append 更改为nconc,因为列表是新鲜的,甚至可以将其更改为collect k and collect v,因为这些可能会表现更好。最终,如果你想用 plist 做这些类似地图的事情,你可能应该考虑使用 hashmaps(可能更快)或 alists(普通列表运算符更适用)。
  • 我认为equal 在比较符号时会退回到eq (ref)。
  • 它只是用eq比较两个东西编译成一条指令,而用equal测试eq的东西几乎和eq一样快,但速度慢得多不是(必须按类型分派并可能递归)

标签: common-lisp plist


【解决方案1】:

没有功能可以做到这一点(这不是人们通常想做的事情)

有一个宏 remf 可以从某个地方的 plist 中删除一个键。

实现此目的的另一种方法是:

(destructuring-bind (&key (a nil ap) (b nil bp) &allow-other-keys) plist
  (append (if ap (list :a a)) (if bp (list :b b))))

但请注意,这仅在您已经知道要保留哪些键并且不会保留 plist 中的顺序并且不会保留重复键(即,如果您的 plist 包含键 :a 多次结果将只包含一次)。

您可以使用普通(符号)键的普通 lambda 列表语法来修改非关键字键。

【讨论】:

  • 谢谢@dan-robertson!我的情况很简单,没想过申请destructuring-bind。好主意!
【解决方案2】:

CL:GET-PROPERTIES 是一个积木:

(defun keep-properties (plist indicator-list &aux k v)
  "Keeps all property list entries for a given indicator-list."
  (loop do (setf (values k v plist)
                 (get-properties plist indicator-list))
        while plist
        collect k collect v
        do (pop plist) (pop plist)))

请注意,最好收集两次,而不是在LOOP 中追加/列出。

【讨论】:

  • 谢谢@rainer-joswig!很高兴了解get-properties 和收集两次的语法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 2021-01-27
  • 2017-10-19
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
相关资源
最近更新 更多