通过路径模式访问clojure.zip树

时间:2018-03-07 06:02:10

标签: clojure pattern-matching zipper

说我有一棵树,我想要访问 - 这应该包括修改被访问项目的可能性 - 所有与路径匹配的项目

(def visit-path [:b :all :x :all])

我使用:all作为通配符来匹配所有子节点。在以下示例树中,

(def my-tree
  {:a "a"
   :b
   {:b-1
    {:x
     {:b-1-1 "b11"
      :b-1-2 "b12"}}
    :b-2
    {:x
     {:b-2-1 "b21"}}}})

这将是项目

  • [:b-1-1“b11”]
  • [:b-1-2“b12”]
  • [:b-2-1“b21”]

使用clojure核心有一种优雅的方法吗?

仅供参考,我通过创建自己的模式访问者

来解决这个问题
(defn visit-zipper-pattern
  [loc pattern f]

但是虽然这个函数通常是可用的,但它非常复杂,它结合了消耗堆栈的递归和尾调用递归。所以当调用像

这样的方法时
(visit-zipper-pattern (map-zipper my-tree) visit-path
  (fn [[k v]] [k (str "mod-" v)]))

使用https://stackoverflow.com/a/15020649/709537中的map-zipper,将树转换为

{:a "a"
 :b {:b-1
     {:x
      {:b-1-1 "mod-b11"
       :b-1-2 "mod-b12"}}
     :b-2
     {:x
      {:b-2-1 "mod-b21"}}}}

1 个答案:

答案 0 :(得分:1)

以下内容将起作用 - 请注意:1)它可能在处理:all键时分配不需要的对象; 2)您需要决定如何处理非映射叶上的:all等边缘情况。< / p>

(defn traverse [[k & rest-ks :as pattern] f tree]
  (if (empty? pattern)
    (f tree)
    (if (= k :all)
      (reduce #(assoc %1 %2 (traverse rest-ks f (get tree %2)))
              tree (keys tree))
      (cond-> tree (contains? tree k)
              (assoc k (traverse rest-ks f (get tree k)))))))

要获得更有效的解决方案,最好按照上面的建议使用https://github.com/nathanmarz/specter