clojure-使用幽灵转换嵌套的数据结构,用几个节点代替一个节点



我正在使用幽灵在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
   ]]]

我不知道的是如何将替换项目拼接到父矢量中,即如何用三个项目替换一个项目,而不是包含三个孩子的项目。

我不知道如何使用Spectre做到这一点,但是这是用clojure.zip进行操作的功能:

(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))))))

您可以用数据结构的拉链调用它,并从要替换为其替换值序列的值的地图将其插入其位置:

(def zipper
  (z/vector-zip [:top
                 [:arbitrary 1 2
                  [:nesting 2 3 [:needle]]]]))
(splice-replace zipper {[:needle] [[:n1] [:n2] [:n3]]})
 => [:top [:arbitrary 1 2 [:nesting 2 3 [:n1] [:n2] [:n3]]]]
(splice-replace zipper {[:nesting 2 3 [:needle]] (range 3 10)})
=> [:top [:arbitrary 1 2 3 4 5 6 7 8 9]]
(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))

我认为应该可以找到一个包含 [: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]]]]

此解决方案使用递归路径,然后使用srange-dynamic将其更新。在"注入/转换"中进行另一次线性搜索并不是很棒的。功能,但似乎没有任何方法可以在当前位置类似于开始或结束的位置。

(transform [(recursive-path [] RECURSE
                        (if-path sequential?
                          (if-path (selected? [ALL (pred= [:needle])])
                            STAY
                            [ALL  RECURSE])))]
       (fn inject [v]  (setval (srange-dynamic
                                #(.indexOf % [:needle])
                                (end-fn [_ start] (inc start)))
                               [[:n1] [:n2] [:n3]] v))
       [:top [:arbitrary 1 2 [:nesting 2 3 [:needle] :more]]])

最新更新