按路径模式访问clojure.zip树
假设我有一个我想访问的树,其中应该包括修改已访问项目的可能性,所有与路径匹配的项目按路径模式访问clojure.zip树,clojure,pattern-matching,zipper,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
(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”]
仅供参考,我通过创建自己的模式访问者解决了这个问题
(defn visit-zipper-pattern
[loc pattern f]
但是,尽管这个函数在一般情况下是可用的,但它非常复杂,既有使用堆栈的递归,也有尾部调用递归。所以当调用这个方法时
(visit-zipper-pattern (map-zipper my-tree) visit-path
(fn [[k v]] [k (str "mod-" v)]))
使用map zippers
from,它将树转换为
{: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)在处理
:所有
关键点时,它可能会分配不需要的对象;2)您需要决定如何处理边缘情况,如非地图叶子上的:所有
(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)))))))
要获得更有效的解决方案,可能最好按照上面的建议使用。您知道吗?谢谢@Clojuremobly这就是我现在使用的解决方案,尽管从specter github页面上看,作者对我来说有点太过自负了。:)谢谢这比我的解决方案更简洁,尽管(也许是因为)它没有使用clojure.zip。与此同时,正如建议的那样,我转而使用specter(虽然是为了灵活性,而不是——在我的情况下——为了性能),但这个问题是关于一个优雅的clojure.core解决方案的——你的解决方案是什么!