如何在Clojure中将有序树变成命名节点的集合?

马丁·OB

我认为最好用一个例子。假设我有一棵有序的树:

(def abcd [:a [:b :c] :d])

我想从中构建键值映射的集合,每个映射代表该树的一个节点,并具有一个随机名称和所有相关信息,即其父级(对于根节点为零)其索引(0、1 ,2 ..),如果它是叶节点,则为其内容(如“:a”)。例如,在这种情况下,可能是:

[{:name G__36654, :parent nil, :index 0}
 {:name G__36655, :content :a, :parent G__36654, :index 0}
 {:name G__36656, :parent G__36654, :index 1}
 {:name G__36657, :content :b, :parent G__36656, :index 0}
 {:name G__36658, :content :c, :parent G__36656, :index 1}
 {:name G__36659, :content :d, :parent G__36654, :index 2}]

我定义了一个函数,该函数似乎可以实现我想要的功能,但是它通过调用自身来使用递归,而我在弄清楚如何使用循环递归方面遇到了麻烦,而且我相信那里肯定会有更好的东西。这是我的尝试:

(defn mttrav "my tree traversal"
  ([ptree parent index]
   (let [name (gensym)]
     (cond
       (not (coll? ptree)) [ {:name name :content ptree :parent parent :index index}]

       :else (reduce into
                     [{:name name  :parent parent :index index}]
                     (map-indexed #(mttrav %2 name  %1) ptree)))))
  ([ptree]
   (mttrav ptree nil  0)))

顺便说一句,我不知道向量是否适合使用正确的集合,也许集合会更有意义,但是我正在使用向量来简化调试,因为当保留生成节点的顺序时,它更具可读性,并且如果节点意外重复,我希望看到它。

提前致谢!

编辑:只是为了澄清,每个节点都有一个:child节点列表而不是:parent节点列表以及其他一些变体也是可以接受的,只要它是地图的平面集合,每个地图代表一个节点,具有唯一的:name,并在此结构中捕获节点的位置,内容和父子关系。预期的输入是打from解析树,这些树通常来自Instaparse,并且映射旨在成为要插入Clara会话中的记录。

腮红

您需要广度优先遍历。因此,如果要在遍历树时构建父级列表,则需要首先唯一地标识所有叶节点。我不确定是否可以这样做,除非您确定您的叶子节点是唯一的。在这里也很晚/很早,所以我的大脑无法最佳工作。我确信我的解决方案会被大量蒸馏掉。

因此,如果您有一棵像[:a [:b:c]:d [:b:c]]的树,则[:b:c]是:b和:c的父级,但是最后两个叶节点也是:b和:c,那么您选择哪个父母?

因此,让我们有一棵树的叶子具有唯一ID的树。

(defn attach-ids [tree]
  (clojure.walk/postwalk (fn [node]
                           (if (coll? node) node
                               {:node node :id (gensym)}))
                         tree))


(def tree (attach-ids [:a [:b :c] :d]))

;; produces this
;; [{:node :a, :id G__21500}
;; [{:node :b, :id G__21501} {:node :c, :id G__21502}]
;;  {:node :d, :id G__21503}]

现在为其余的解决方案


(defn add-parent [parent-map id branch]
  (assoc parent-map id {:children-ids (set (map :id branch))
                        :child-nodes (map :node branch)}))


(defn find-parent-id [node parent-map]
  (->> parent-map
    (filter (fn [[parent-id {children-ids :children-ids}]]
              (contains? children-ids (:id node))))
    ffirst))


(defn find-index [node parent-map tree]
  (if-let [parent-id (find-parent-id node parent-map)]
    (let [children (:child-nodes (get parent-map parent-id))]
      (.indexOf children (:node node)))
    (.indexOf tree node)))


(defn bfs [tree]
  (loop [queue tree
         parent-map {}
         ret []]
    (if (not-empty queue)
      (let [node (first queue)
            rst (vec (rest queue))]
        (cond
          (map? node)
          (recur rst
                 parent-map
                 (conj ret (assoc node :parent (find-parent-id node parent-map) 
                                       :index (find-index node parent-map tree))))
          (vector? node)
          (let [parent-id (gensym)]
            (recur (into rst node)
                   (add-parent parent-map parent-id node)
                   (conj ret {:id parent-id 
                              :index (find-index node parent-map tree)
                              :parent (find-parent-id node parent-map)})))))
      ret)))




(def tree  (attach-ids [:a [:b :c] :d]))
(bfs tree)

;; children with :parent nil value point to root
;;[{:node :a, :id G__21504, :parent nil, :index 0}
;; {:id G__21513, :index 1}
;; {:node :d, :id G__21507, :parent nil, :index 2}
;; {:node :b, :id G__21505, :parent G__21513, :index 0}
;; {:node :c, :id G__21506, :parent G__21513, :index 1}]

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章