尝试在clojure中对嵌套结构中的值进行“分组”和展平

时间:2014-02-02 01:06:41

标签: clojure

另一个不安的夜晚试图找出如何做到这一点。鉴于嵌套数据结构,我为了便于阅读而重新格式化,以寻找格式错误的声明:

(def tm (list 
          {:a 1 :b 2 :c 3 :child (list 
               {:a 4 :b 5 :c 6 :child (list 
                     {:a 40 :b 50 :c 60 :child (list {:a nil :b nil :c nil})})}                                                                                               
               {:a 70 :b 80 :c 90 :child (list {:a nil :b nil :c nil})})} 
          {:a 400 :b 500 :c 600 :child (list {:a nil :b nil :c nil})}
          {:a 7 :b 8 :c 9 :child (list 
               {:a 10 :b 11 :c 12 :child (list {:a nil :b nil :c nil})})})
  )

遍历可以在 dg123 示例下面看到

我得到以下内容:

=>(roll-down tm :a)
((1 4 40) (1 70) (7 10))

但是期待:

((1 4 40) (1 70) (400) (7 10))

4 个答案:

答案 0 :(得分:2)

这是一个基于拉链的解决方案

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

(defn root-to-leaf-paths [t]
   (loop [t t paths []] 
     (cond 
       (z/end? t) paths
       (z/branch? t) (recur (z/next t) paths) 
       :leaf (recur (z/next t) (conj paths (z/path t))))))

(defn roll-down [d kw]
   (->> (z/zipper :child :child nil {:child d})
        (root-to-leaf-paths)
        (map (comp rest (partial map kw)))))

注意:您的tm不是树,而是树列表。所以,在这里我们首先通过将树列表放在一个父根{:child d}下来将此结构转换为树。然后我们从结果路径(comp rest ...)中删除它。

实施例

(roll-down tm :a)
;=> ((1 4 40) (1 70) (400) (7 10))

(roll-down tm :b)
;=> ((2 5 50) (2 80) (500) (8 11))

(roll-down tm :c)
;=> ((3 6 60) (3 90) (600) (9 12))

答案 1 :(得分:2)

简短版使用mapcat

(defn roll-down [g k]
  (mapcat
   #(if-let [c (:child %)]
      (map (fn [x] (conj x (k %))) (roll-down c k))
      [[(k %)]])
   g))

-> (roll-down tm :a)
([nil 40 4 1] [nil 70 1] [nil 400] [nil 10 7])

此解决方案使用深度优先遍历,并将nil视为路径。如果您愿意,可以在结果上执行removereverse

答案 2 :(得分:1)

您可以简单地将提取器映射到数据

(defn get-as [tm] 
  (let [results (atom [])]
    (clojure.walk/prewalk
     #(do (if-let [uid (:a %)] 
            (swap! results conj :a (:a %))) 
          %)
     tm)
    @results))

(map get-as tm) ; => ([:a 1 :a 4] [:a 7 :a 10])

答案 3 :(得分:1)

这是一种不使用可变状态的更实用的方法:

(defn roll-down-helper [c kw]
  (when-let [root-val (kw c)]
    (cons root-val
          (filter (comp not nil?)
                  (for [node (:child c)]
                    (roll-down-helper node kw))))))

(defn get-paths [[x & ys]]
  (if (empty? ys)
    (list (list x))
    (for [y ys]
      (flatten (cons x y)))))


(defn roll-down [c kw]
  (apply concat
   (map (comp get-paths #(roll-down-helper % kw)) c)))

然后您可以执行以下操作:

user> (roll-down tm :a)
((1 4 40) (1 70) (400) (7 10))
user> (roll-down tm :b)
((2 5 50) (2 80) (500) (8 11))
user> (roll-down tm :c)
((3 6 60) (3 90) (600) (9 12))