ClojureDocs

Nav

Namespaces

tree-seq

clojure.core

Available since 1.0 (source)
  • (tree-seq branch? children root)
Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
 branch? must be a fn of one arg that returns true if passed a node
 that can have children (but may not).  children must be a fn of one
 arg that returns a sequence of the children. Will only be called on
 nodes for which branch? returns true. Root is the root node of the
tree.
10 Examples
;; Each node is a number or a seq, 
;; so branch?==seq? and children==identity
;; 
;;     .
;;    / \
;;   .   .
;;  /|\  |
;; 1 2 . 4
;;     |  
;;     3
;;
;; ... each sub-tree is serialized in depth-first order
(tree-seq seq? identity '((1 2 (3)) (4)))

;;=> (((1 2 (3)) (4)) 
;;     (1 2 (3)) 
;;        1 
;;        2 
;;        (3) 
;;            3 
;;     (4) 
;;        4)
(tree-seq map? #(interleave (keys %) (vals %)) {:a 1 :b {:c 3 :d 4 :e {:f 6 :g 7}}})
;;=> ({:a 1, :b {:c 3, :d 4, :e {:f 6, :g 7}}} 
;;     :a 1 :b {:c 3, :d 4, :e {:f 6, :g 7}} 
;;              :c 3 :d 4 :e {:f 6, :g 7}  
;;                            :f 6 :g 7)
;; Each node is a (node-root child1 child2 ...),
;; so branch?==next and children==rest
;;
;;     A
;;    / \
;;   B   C
;;  / \  |
;; D   E F
;;
(map first (tree-seq next rest '(:A (:B (:D) (:E)) (:C (:F)))))
;;=> (:A :B :D :E :C :F)
;; The previous example seems to be a flatten function,
;; where every node's value is added to the returned sequence
;; as it is visited in a depth first search.
;; That is not the case as the following example illustrates: 
;;      *
;;     / \
;;    *   4
;;   /|\
;;  1 2 *
;;      |
;;      3
(map first (tree-seq next rest '((1 2 (3)) (4))))
;;=> ((1 2 (3)) 4)

;; For a proper 'flatten' function see below.
;; The nodes are filtered based on their collection type, 
;; here they must be a list.
(tree-seq seq? seq [[1 2 [3]] [4]])
;;=> ([[1 2 [3]] [4]])

;; notice the difference between seq? and sequential?
(tree-seq sequential? seq [[1 2 [3]] [4]])
;;=> ([[1 2 [3]] [4]] [1 2 [3]] 1 2 [3] 3 [4] 4)

;; If the nodes in the tree are a specific type of collection...
;; a vector
(tree-seq vector? seq [[1 2 [3]] [4]])
([[1 2 [3]] [4]] [1 2 [3]] 1 2 [3] 3 [4] 4)

;; ... or a list
(tree-seq seq? seq '((1 2 (3)) (4)))
(((1 2 (3)) (4)) (1 2 (3)) 1 2 (3) 3 (4) 4)
;; Use tree-seq to recursively find all files 
;; given a root directory (here for didactic purposes. See file-seq)
(let [directory (clojure.java.io/file "/path/to/directory/")
      dir? #(.isDirectory %)]
    ;we want only files, therefore filter items that are not directories.
    (filter (comp not dir?) 
          (tree-seq dir? #(.listFiles %) directory)))

(def t '((1 2 (3)) (4)))
;;=> #'user/t

;; Here the tree-seq uses 'sequential?' as the 'branch?' predicate.
;; This causes the 'children' function to run for any collection.
;; The 'seq' ('children') function recurses on all items in the collection.
;; This results in a sequence of sub-trees, rooted at each node.
(tree-seq sequential? seq t)
;;=> (((1 2 (3)) (4))
;;    (1 2 (3)) 1 2 
;;    (3) 3 (4) 4)

;; (The following example is from 4Clojure)
;; It returns the leaves of a tree as a sequence.
;; 
(defn flatten [x]
  (filter (complement sequential?)
          (rest (tree-seq sequential? seq x))))
(flatten t)
;;=> (1 2 3 4)
(tree-seq seq? identity '(1 2 (3 (4))))
;; ((1 2 (3 (4))) 1 2 (3 (4)) 3 (4) 4)

;; It's same as ...
(tree-seq seq? seq '(1 2 (3 (4))))
(tree-seq sequential? seq '(1 2 (3 (4))))
;; ((1 2 (3 (4))) 1 2 (3 (4)) 3 (4) 4)

;; This processing ...
(sequential? '(1 2 (3 (4)))) ;; returns true ... -> (1 2 (3 (4))) <--- !!!
(sequential? 1)              ;; returns false ... -> 1
(sequential? 2)              ;; returns false ... -> 2
(sequential? '(3 (4))        ;; returns true  ... -> (3 (4))     <--- !!! 
(sequential? 3)              ;; returns false ... -> 3
(sequential? '(4))           ;; returns true  ... -> (4)         <--- !!!
(sequential? 4)              ;; return false  ... -> 4

;; so, #tree-seq returns...
;; ((1 2 (3 (4))) 1 2 (3 (4)) 3 (4) 4)
;; When traversing a map tree, it's sometimes useful to have a back-reference
;; to the parent.  This tree-seq variant provides that.

(defn tree-seq-adding-parent
  "Like tree-seq, but takes in a tree of maps and a unique :parent key to each map."
  [branch? children root]
  (let [walk (fn walk [parent node]
               (lazy-seq
                (cons (assoc node :parent parent)
                 (when (branch? node)
                   (mapcat (partial walk node) (children node))))))]
    (walk nil root)))

(def map-tree
  {:name "root"
   :children [{:name "child1"
               :children [{:name "grandchild1"}
                          {:name "grandchild2"}]}
              {:name "child2"}]})

(tree-seq-adding-parent associative? :children map-tree)
;; =>
;; ({:name "root",
;;   :children
;;   [{:name "child1",
;;     :children [{:name "grandchild1"} {:name "grandchild2"}]}
;;    {:name "child2"}],
;;   :parent nil
;;  {:name "child1",
;;   :children [{:name "grandchild1"} {:name "grandchild2"}],
;;   :parent
;;   {:name "root",
;;    :children
;;    [{:name "child1",
;;      :children [{:name "grandchild1"} {:name "grandchild2"}]}
;;     {:name "child2"}]}}
;;  {:name "grandchild1",
;;   :parent
;;   {:name "child1",
;;    :children [{:name "grandchild1"} {:name "grandchild2"}]}}
;;  {:name "grandchild2",
;;   :parent
;;   {:name "child1",
;;    :children [{:name "grandchild1"} {:name "grandchild2"}]}}
;;  {:name "child2",
;;   :parent
;;   {:name "root",
;;    :children
;;    [{:name "child1",
;;      :children [{:name "grandchild1"} {:name "grandchild2"}]}
;;     {:name "child2"}]}})

;; Tree-seq uses depth-first search (DFS), pre-order traversal, to explore the 
;; tree. 
;;
;;     1
;;    / \
;;   2   3
;;   |  
;;   4
;;   |
;;   5

(let [tree {:value 1 
            :children [{:value    2
                        :children [{:value    4
                                    :children [{:value 5}]}]}
                       {:value 3}]}]
     (map :value
       (tree-seq
         #(contains? % :children) ;branch?
         #(get % :children) ;children
         tree)))

=> (1 2 4 5 3)
See Also

Traverses form, an arbitrary data structure. inner and outer are functions. Applies inner to eac...

Added by phreed

A tree seq on java.io.Files

Added by reborg
2 Notes
    By , created 9.2 years ago, updated 7.5 years ago

    The 'branch?' and 'children' functions perform different types of filtering. The 'branch?' function examines a node and determines whether there are children that need to be included in the processing. The 'children' function selects (or generates) the children to be included in the returned sequence.

    By , created 8.9 years ago

    Example 5 has for the second example (tree-seq seq? seq [[1 2 [3]] [4]]) when it should be (tree-seq sequential? seq [[1 2 [3]] [4]])