【问题标题】:Tree traversal in ClojureClojure 中的树遍历
【发布时间】:2016-07-26 20:33:14
【问题描述】:

考虑一棵树,它由以下递归定义定义:

一棵树应该是一个包含两个元素的向量:第一个是一个数字,第二个是一个树列表或 nil。

以下 clojure 数据结构就是一个示例:

(def tree '[9 ([4 nil]
               [6 nil]
               [2 ([55 nil]
                   [22 nil]
                   [3 ([5 nil])])]
               [67 ([44 nil])])])

这个结构应该被转换成所有可能的向下连接的列表,可以从任何节点到它的子节点。连接应表示为包含父节点值和子节点值的向量。顺序不重要:

(def result '([9 4]
              [9 6]
              [9 2]
              [2 55]
              [2 22]
              [2 3]
              [3 5]
              [9 67]
              [67 44])

我想出了这个解决方案:

(defn get-connections [[x xs]]
  (concat (map #(vector x (first %)) xs)
          (mapcat get-connections xs)))

而且,确实:

(= (sort result)
   (sort (get-connections tree)))
;; true

但是,有没有更好的方法来使用普通的 clojure? 在这种方法中,我遍历每个节点的子节点两次,应该避免这种情况。在这种特殊情况下,不需要尾递归,所以一个简单的递归版本就可以了。

此外,我很感兴趣可以使用哪些更高级别的抽象来解决这个问题。那么 Zippers 或 Clojure/Walk 呢?最后:哪些技术也可以在 ClojureScript 中使用?

【问题讨论】:

    标签: clojure tree clojurescript


    【解决方案1】:

    您可以尝试将列表理解 + tree-seq:

    user> (for [[value children] (tree-seq coll? second tree)
                [child-value] children]
            [value child-value])
    
    ;;=> ([9 4] [9 6] [9 2] [9 67] [2 55] [2 22] [2 3] [3 5] [67 44])
    

    这应该在 cljs 中可用。

    据我所知,clojurescript 中提供了 zippers 和 clojure.walk,但实际上您不需要它们来完成这项琐碎的任务。我猜tree-seq 相当地道。

    双遍历怎么样,你可以像这样轻松地将它重新排列成一个:

    (defn get-connections [[x xs]]
      (mapcat #(cons [x (first %)] (get-connections %)) xs))
    
    user> (get-connections tree)
    ;;=> ([9 4] [9 6] [9 2] [2 55] [2 22] [2 3] [3 5] [9 67] [67 44])
    

    然后你可以添加惰性,使这个解决方案真正地道:

    (defn get-connections [[x xs]]
      (mapcat #(lazy-seq (cons [x (first %)] (get-connections %))) xs))
    

    【讨论】:

    • 之前没有时间查看这个答案。感谢您提供出色的解决方案。他们完美地工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多