我正在使用specter来转换Clojure中的嵌套数据结构,但是我还没有掌握它。特别是,我正在尝试创建一个转换,该转换将查找与谓词匹配的任意深度的项目,并将其替换为多个项目。
[:top
[:arbitrary 1 2
[:nesting
2
3
[:needle] ; <-- the thing to find
]]]
-->
[:top
[:arbitrary 1 2
[:nesting
2
3
[:n1] [:n2] [:n3] ; <-- 3 items inserted in the place of 1
]]]
我不知道如何将替换项拼接到父向量中,即,如何用三项而不是包含三个子项的项来替换一项。
答案 0 :(得分:3)
我不知道如何使用Spectre来做到这一点,但这是一个使用clojure.zip的功能:
path
您可以使用数据结构的拉链和从要替换的值到其替换值的序列进行映射的映射方式来对其进行命名:
(defn splice-replace [zipper smap]
(loop [loc zipper]
(if (z/end? loc)
(z/root loc)
(recur
(z/next
(if-let [sub (smap (z/node loc))]
(reduce (comp z/right z/insert-right)
(z/replace loc (first sub))
(rest sub))
loc))))))
答案 1 :(得分:3)
(defn replace-needle [input replacement]
(let [needle-parent? #(= % [:needle])
NEEDLE-PARENT (recursive-path
[] p (cond-path
#(and (vector? %) (some needle-parent? %)) [(continue-then-stay [ALL p])]
vector? [ALL p]))
inject-replacement (fn inject [x] (vec (mapcat #(if (needle-parent? %) replacement [%]) x)))]
(transform [NEEDLE-PARENT] inject-replacement input)))
(let [input [:top
[:arbitrary 1 2
[:nesting 2 3 [:needle]]]]
replacement [[:n1] [:n2] [:n3]]]
(replace-needle input replacement))
答案 2 :(得分:1)
我认为应该可以找到包含[:needle]
的向量,然后找到[:needle]
的索引,然后使用srange
将新元素拼接到父元素上该索引,但我没有找到使用Spectre的方法。
以下是使用clojure.walk
表达的相同想法:
(require '[clojure.walk :refer [postwalk]])
(postwalk (fn [node]
(if (and (vector? node)
(some (partial = [:needle]) node))
(let [idx (.indexOf node [:needle])]
(vec (concat (take idx node)
[[:n1] [:n2] [:n3]]
(drop (inc idx) node))))
node))
data)
;; => [:top [:arbitrary 1 2 [:nesting 2 3 [:n1] [:n2] [:n3]]]]