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