在嵌套树结构的每个分支中插入所有父分支的总和

时间:2014-04-11 13:11:02

标签: clojure

在树结构中,如何将键/值与每个分支合并,其中值是分支值和所有父分支值的总和?

例如,从跟随树结构开始:

[{  :v 1
    :_ [{ :v 2 }
        { :v 3
          :_ [{ :v 5 }]}
        { :v 4 }]}]

如何将其重新创建为:

[{  :v 1
    :sum 1
    :_ [{ :v 2
          :sum 3 }
        { :v 3
          :sum 4
          :_ [{ :v 5
                :sum 9 }]}
        { :v 4
          :sum 5 }]}]

我一直在尝试使用walk。我认为这可能是正确的做法。但是,到目前为止,我还没有管理它。

3 个答案:

答案 0 :(得分:4)

我认为这种递归函数可以解决问题。

(defn sums [{v :v children :_} sum]
  {:v v
   :sum (+ sum v)
   :_ (mapv #(sums % (+ sum v)) children)})

按以下方式评估时:

(def root
   [{:v 1
      :_ [{:v 2}
          {:v 3
           :_ [{:v 5}]}
          {:v 4}]}])    

[(sums (first root) 0)]

结果:

[{:v 1,
  :sum 1,
  :_ [{:v 2,
       :sum 3,
       :_ []}
      {:v 3,
       :sum 4,
       :_ [{:v 5,
            :sum 9,
            :_ []}]}
      {:v 4,
       :sum 5,
       :_ []}]}]

或者这是您树格式的相同sums函数的更友好版本。

(defn sums [root]
  (letfn [(f [{v :v children :_} sum]
            {:v v
             :sum (+ sum v)
             :_ (mapv #(f % (+ sum v)) children)})]
    [(f (first root) 0)]))

(sums root)
;= same result as before

答案 1 :(得分:2)

为了便于比较,这是一个使用clojure.walk的版本。我认为这是一种定制的递归函数比使用walk更干净的情况。自定义函数允许您将中间结果(以父项的总和的形式)从父对象传递给子作为函数参数,而传递给walk的函数除了被遍历的表单之外没有额外的参数,所以在遍历树时,您必须在数据本身中记录中间结果。

(require '[clojure.walk :as walk])

(defn sums
  [x]
  (walk/prewalk (fn [m]
                  (if (map? m)
                    (let [v (or (:v m) 0)
                          s (+ v (or (:sum m) 0))
                          m (assoc m :sum s)]
                      (if (seq (:_ m))
                        (update-in m [:_]
                                   (partial map (fn [c] (assoc c :sum s))))
                        m))
                    m))
                x))

答案 2 :(得分:1)

可以编写拉链解决方案

(require '[clojure.zip :as z])

(def root
  [{:v 1
    :_ [{:v 2}
        {:v 3
         :_ [{:v 5}]}
        {:v 4}]}])    

(def zipper (partial z/zipper :_ :_ (fn [n ch] (assoc n :_ (vec ch)))))

(loop [node (zipper (first root))] 
  (if (z/end? node) 
    (z/root node) 
    (recur 
      (z/next 
       (z/edit node 
                 #(assoc % :sum ((fnil + 0) %2 (:v %))) 
                 (some-> node z/up z/node :sum))))))

;=> {:v 1, 
     :_ [{:v 2, :sum 3}
         {:v 3, 
          :_ [{:v 5, :sum 9}], 
          :sum 4} 
         {:v 4, :sum 5}], 
     :sum 1}

如果需要,可以将其回收到矢量中。注意," root"当包含在向量中时,定义的树结构的其余部分不匹配,这就是为什么我们有一个"(第一个根)"。