tree-seq

added
1.0

ns
clojure.core

type
function

(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.

                ;; 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
(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 x)
;;=> (((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)