I want to create a function that collects the value from each node in a binary tree. In the ClojureDocs, I found several functions for traversing a tree/graph, such as tree-seq, prewalk, and postwalk.
https://clojuredocs.org/clojure.core/tree-seq
https://clojuredocs.org/clojure.walk/prewalk
https://clojuredocs.org/clojure.walk/postwalk
Can any of these be used to accumulate the value of nodes traversed? As a Clojure newby, I don't see how to do it. If you know how (in Clojure or similar Lispy language), please show me. Ideally, your answer will be understandable by a Clojure newby;-)
My binary tree is represented with nodes like this: (value left-child right-child). For example:
( 2 (7 nil nil) (88 (5 nil nil) nil) )
From this example, I'd like the function to return (2 7 88 5).
NOTE: The traversal method isn't important. I just want to learn a technique for collecting node values.
Well,
tree-seq
will give you the node sequence (of a depth first walk). You can then do any other transformation on it, including(map some-value-extractor-fn (tree-seq ...
to get the values in each node. You just need to pick a tree representation and appropriate functions for that representation sotree-seq
can know what is an internal node and what its children are. For instance, using your definition of the tree as a nested list:Nested list tree
The nodes where our tree could branch are lists, which we can identify using
list?
.Their children are the values following the first, i.e. theirrest
. So we can define the tree-seq using only standard functions:but this has a bit of garbage - each
nil
appears as a member of the seq, each value we're interested in appears both in its list node and as a member in itself and so on. We can clean this up with afilter
orremove
- for instance we can discard all the leaf values and take only internal nodes:and then just
map
first
over those:Alternatively we could try to discard internal and nil nodes of the tree, taking only leaves with a value:
Note that in this strategy I had to use
seq
rather thanrest
, as I want the first value in a list to also be a child of that node.(some-fn list? nil?)
is a bit of higher order functions - it builds a function that checks if the input satisfies either of the predicateslist?
ornil?
(or both).Nested map tree
If you want a maybe more general tree definition, where each node can contain multiple values plus a variable number of children, you can define your tree as nested maps:
{:value 2 :children [{:value 7} {:value 88 :children [{:value 5}]}]}
In this case, looking at only the maps as nodes is generally simplest. Our possibly branching nodes are maps - check for that with
map?
. We stored their children in the key:children
, which is a keyword and so is also a function. We use that function to get the children.and then you just need to
map
over the nodes to get the data you want out of them:for accumulating node values of a tree, i have a non-functional solution: