Reputation: 497
I'm trying to combine recursion and yield to in-order traverse a tree
This is what I'm currently having. However, when I try to traverse the tree, it seems to only traverse the root node
class Tree:
...
def post_order(self, node: TreeNode):
"""Yield next node in post order from node"""
for child in node.get_children():
self.post_order(child)
yield node
if __name__ == '__main__':
root = TreeNode('root')
depth1a = TreeNode('1a')
depth1b = TreeNode('1b')
root.add_children(depth1a, depth1b)
tree = Tree(root)
for node in tree.post_order(root):
print(node.get_element())
When I run the code, it only prints out
root
which is the element of the first node, not what I want which is
1a
1b
root
Does anyone have an idea what I did wrong?
Thanks everyone
Upvotes: 2
Views: 760
Reputation: 135415
Mike Pham's answer is great but I wanted to share a back-tracking approach that helps us understand how we can manually build the desired sequence using direct recursion instead of for
loops. It's not a better program; it's an exercise to check your mastery of generators -
from functools import reduce
def empty ():
yield from ()
def postorder (node, backtrack = empty(), visit = False):
if visit:
yield node.data
yield from backtrack
elif node.children:
yield from reduce \
( lambda it, child: postorder (child, it)
, node.children[::-1]
, postorder (node, backtrack, True)
)
else:
yield from postorder (node, backtrack, True)
Test it out -
class node:
def __init__ (self, data, *children):
self.data = data
self.children = children
tree = \
node("a",
node("b",
node("e"),
node("f",
node("k"))),
node("c"),
node("d",
node("g"),
node("h"),
node("i"),
node("j")))
print(list(postorder(tree)))
# [ 'e', 'k', 'f', 'b', 'c', 'g', 'h', 'i', 'j', 'd', 'a' ]
This might help give you an appreciation for what yield
actually does for you. Here's the same program without it. Minor differences in bold -
def empty ():
return []
def postorder (node, backtrack = empty, visit = False):
def gen ():
if visit:
return [ node.data ] + backtrack()
elif node.children:
return reduce \
( lambda it, child: postorder (child, it)
, node.children[::-1]
, postorder (node, backtrack, True)
) ()
else:
return postorder (node, backtrack, True) ()
return gen
def run (gen):
return gen ()
print(run(postorder(tree)))
# [ 'e', 'k', 'f', 'b', 'c', 'g', 'h', 'i', 'j', 'd', 'a' ]
Upvotes: 0
Reputation: 497
Thanks to 张实唯, turns out I have to use yield from
. Calling a generator function does not yield from it:
class Tree:
...
def post_order(self, node: TreeNode):
"""Yield next node in post order from node"""
for child in node.get_children():
yield from self.post_order(child)
yield node
Upvotes: 2