Clojure 中的树遍历
Tree traversal in Clojure
考虑一棵树,它由以下递归定义定义:
树应该是两个元素的向量:第一个是数字,第二个是树列表或 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 中也可以使用哪些技术?
您可以尝试列表理解 + 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 中可用。
据我所知,拉链和 clojure.walk 都可以在 clojurescript 中使用,但实际上您不需要它们来完成这项琐碎的任务。我想 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))
考虑一棵树,它由以下递归定义定义:
树应该是两个元素的向量:第一个是数字,第二个是树列表或 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 中也可以使用哪些技术?
您可以尝试列表理解 + 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 中可用。
据我所知,拉链和 clojure.walk 都可以在 clojurescript 中使用,但实际上您不需要它们来完成这项琐碎的任务。我想 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))