Open main menu
Home
Random
Recent changes
Special pages
Community portal
Preferences
About Wikipedia
Disclaimers
Incubator escapee wiki
Search
User menu
Talk
Dark mode
Contributions
Create account
Log in
Editing
Corecursion
(section)
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
=== Tree traversal === [[Tree traversal]] via a [[depth-first]] approach is a classic example of recursion. Dually, [[breadth-first]] traversal can very naturally be implemented via corecursion. Iteratively, one may traverse a tree by placing its root node in a data structure, then iterating with that data structure while it is non-empty, on each step removing the first node from it and placing the removed node's ''child nodes'' back into that data structure. If the data structure is a [[Stack (abstract data type)|stack]] (LIFO), this yields depth-first traversal, and if the data structure is a [[Queue (abstract data type)|queue]] (FIFO), this yields breadth-first traversal: :<math>\text{trav}_\text{nn}(t) = \text{aux}_\text{nn}([t])</math> :<math>\text{aux}_\text{df}([t,\ ...ts]) = \text{val}(t) ;\ \text{aux}_\text{df}([\ ...\text{children}(t),\ ...ts\ ])</math> :<math>\text{aux}_\text{bf}([t,\ ...ts]) = \text{val}(t) ;\ \text{aux}_\text{bf}([\ ...ts,\ ...\text{children}(t)\ ])</math> Using recursion, a depth-first traversal of a tree is implemented simply as recursively traversing each of the root node's child nodes in turn. Thus the second child subtree is not processed until the first child subtree is finished. The root node's value is handled separately, whether before the first child is traversed (resulting in pre-order traversal), after the first is finished and before the second (in-order), or after the second child node is finished (post-order) {{--}} assuming the tree is binary, for simplicity of exposition. The call stack (of the recursive traversal function invocations) corresponds to the stack that would be iterated over with the explicit LIFO structure manipulation mentioned above. Symbolically, :<math>\text{df}_\text{in}(t) = [\ ...\text{df}_\text{in}(\text{left}(t)),\ \text{val}(t),\ ...\text{df}_\text{in}(\text{right}(t))\ ]</math> <!-- :<math>\text{df}_\text{pre}(t) = [\ \text{val}(t),\ ...flatMap(\text{df}_\text{pre})(\text{children}(t))\ ]</math> --> :<math>\text{df}_\text{pre}(t) = [\ \text{val}(t),\ ...\text{df}_\text{pre}(\text{left}(t)),\ ...\text{df}_\text{pre}(\text{right}(t))\ ]</math> :<math>\text{df}_\text{post}(t) = [\ ...\text{df}_\text{post}(\text{left}(t)),\ ...\text{df}_\text{post}(\text{right}(t)),\ \text{val}(t)\ ]</math> "Recursion" has two meanings here. First, the recursive invocations of the tree traversal functions <math>\text{df}_{xyz}</math>. More pertinently, we need to contend with how the resulting ''list of values'' is built here. Recursive, bottom-up output creation will result in the right-to-left tree traversal. To have it actually performed in the intended left-to-right order the sequencing would need to be enforced by some extraneous means, or it would be automatically achieved if the output were to be built in the top-down fashion, i.e. ''corecursively''. A breadth-first traversal creating its output in the top-down order, corecursively, can be also implemented by starting at the root node, outputting its value,{{efn|Breadth-first traversal, unlike depth-first, is unambiguous, and visits a node value before processing children.}} then breadth-first traversing the subtrees β i.e., passing on the ''whole list'' of subtrees to the next step (not a single subtree, as in the recursive approach) β at the next step outputting the values of all of their root nodes, then passing on ''their'' child subtrees, etc.{{efn|Technically, one may define a breadth-first traversal on an ordered, disconnected set of trees β first the root node of each tree, then the children of each tree in turn, then the grandchildren in turn, etc.}} In this case the generator function, indeed the output sequence itself, acts as the queue. As in the factorial example above, where the auxiliary information of the index (which step one was at, ''n'') was pushed forward, in addition to the actual output of ''n''!, in this case the auxiliary information of the remaining subtrees is pushed forward, in addition to the actual output. Symbolically, :<math>v, ts = ([], [\text{FullTree}]) : (\text{RootValues}(ts), \text{ChildTrees}(ts))</math> meaning that at each step, one outputs the list of values in this level's nodes, then proceeds to the next level's nodes. Generating just the node values from this sequence simply requires discarding the auxiliary child tree data, then flattening the list of lists (values are initially grouped by level (depth); flattening (ungrouping) yields a flat linear list). This is extensionally equivalent to the <math>\text{aux}_\text{bf}</math> specification above. In Haskell, <syntaxhighlight lang=haskell> concatMap fst ( (\(v, ts) -> (rootValues ts, childTrees ts)) `iterate` ([], [fullTree]) )</syntaxhighlight> Notably, given an infinite tree,{{efn|Assume fixed [[branching factor]] (e.g., binary), or at least bounded, and balanced (infinite in every direction).}} the corecursive breadth-first traversal will traverse all nodes, just as for a finite tree, while the recursive depth-first traversal will go down one branch and not traverse all nodes, and indeed if traversing post-order, as in this example (or in-order), it will visit no nodes at all, because it never reaches a leaf. This shows the usefulness of corecursion rather than recursion for dealing with infinite data structures. One caveat still remains for trees with the infinite branching factor, which need a more attentive interlacing to explore the space better. See [[Dovetailing (computer science)|dovetailing]]. In Python, this can be implemented as follows.{{efn|First defining a tree class, say via: <syntaxhighlight lang="python"> class Tree: def __init__(self, value, left=None, right=None): self.value = value self.left = left self.right = right def __str__(self): return str(self.value) </syntaxhighlight> and initializing a tree, say via: <syntaxhighlight lang="python"> t = Tree(1, Tree(2, Tree(4), Tree(5)), Tree(3, Tree(6), Tree(7))) </syntaxhighlight> In this example nodes are labeled in breadth-first order: 1 2 3 4 5 6 7 }} The usual post-order depth-first traversal can be defined as:{{efn|Intuitively, the function iterates over subtrees (possibly empty), then once these are finished, all that is left is the node itself, whose value is then returned; this corresponds to treating a leaf node as basic.}} <syntaxhighlight lang="python"> def df(node): """Post-order depth-first traversal.""" if node is not None: df(node.left) df(node.right) print(node.value) </syntaxhighlight> This can then be called by <code>df(t)</code> to print the values of the nodes of the tree in post-order depth-first order. The breadth-first corecursive generator can be defined as:{{efn|1=Here the argument (and loop variable) is considered as a whole, possible infinite tree, represented by (identified with) its root node (tree = root node), rather than as a potential leaf node, hence the choice of variable name.}} <syntaxhighlight lang="python"> def bf(tree): """Breadth-first corecursive generator.""" tree_list = [tree] while tree_list: new_tree_list = [] for tree in tree_list: if tree is not None: yield tree.value new_tree_list.append(tree.left) new_tree_list.append(tree.right) tree_list = new_tree_list </syntaxhighlight> This can then be called to print the values of the nodes of the tree in breadth-first order: <syntaxhighlight lang="python"> for i in bf(t): print(i) </syntaxhighlight>
Edit summary
(Briefly describe your changes)
By publishing changes, you agree to the
Terms of Use
, and you irrevocably agree to release your contribution under the
CC BY-SA 4.0 License
and the
GFDL
. You agree that a hyperlink or URL is sufficient attribution under the Creative Commons license.
Cancel
Editing help
(opens in new window)