This article has been dead for over three months
You
#!/usr/bin/env python
# -*-coding: utf8-*-
# Title: walktree.py
# Author: Gribouillis for the python forum at www.daniweb.com
# Created: 2011-11-18 23:28:39.608291 (isoformat date)
# License: Public Domain
# Use this code freely.
"""This module implements a generic depth first tree traversal.
"""
from __future__ import print_function
from collections import deque
# IMPORTANT NOTE: this module needs the class ConstSequence
# from http://www.daniweb.com/software-development/python/code/395101
from constsequence import ConstSequence
version_info = (1, 0)
version = ".".join(map(str, version_info))
PREORDER, INORDER, POSTORDER = tuple(range(3))
_innernode = object()
def walk(node, gen_subnodes, order=PREORDER, reverse_path = False):
"""Traverse a tree based at 'node' and generate a sequence of paths
in the tree from the root node to the visited node.
Typical use:
for path in walk(node, func):
visited = path[-1]
if path.is_leaf:
print(visited, "is a leaf node!")
The generated 'path' is a read-only sequence with path[0] being
the base node of the walk and path[-1] being the visited node. If
reverse_path is set to True, the path will appear from right to left,
with the visited node in position 0. During the whole walk, the function
generates the same path object, but in a different state. At any step,
path.is_leaf is a boolean indicating that the visited node is a leaf
in the tree.
The arguments are
@ node : an arbitrary python object
@ gen_subnodes : a function defining the tree structure. It must have
the interface gen_subnodes(node) --> iterable containing other nodes.
this function will be called with the initial node and the descendent
nodes that it generates through this function.
@ order: an integer with possible values PREORDER, INORDER, POSTORDER which
are defined in this module.
@ reverse_path: a boolean indicating that the path should be read
from right to left.
"""
indexes = ((2, 0, 1), (2, 1, 0), (1, 2, 0))[order]
todo = deque((iter((node,)),))
path = deque()
const_path = ConstSequence(path)
if reverse_path:
ppush, ppop = path.appendleft, path.popleft
else:
ppush, ppop = path.append, path.pop
while todo:
sequence = todo.pop()
if sequence is None:
ppop()
elif sequence is _innernode:
const_path.is_leaf = False
yield const_path
else:
try:
node = next(sequence)
except StopIteration:
pass
else:
todo.append(sequence)
ppush(node)
todo.append(None)
sub = iter(gen_subnodes(node))
try:
left = next(sub)
except StopIteration:
const_path.is_leaf = True
yield const_path
else:
t = (iter((left,)), _innernode, sub)
todo.extend(t[i] for i in indexes)
if __name__ == "__main__":
# an example tree
root = (
((1,2), (4,5), 6),
(7, (9)),
)
# a function to generates subnodes for our tree
def subn(node):
if isinstance(node, tuple):
return node
else:
return ()
# using the walk() generator on our tree
for path in walk(root, subn, POSTORDER):
print(("leaf " if path.is_leaf else "inner"), list(path))
""" example code output --->
leaf [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6), (1, 2), 1]
leaf [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6), (1, 2), 2]
inner [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6), (1, 2)]
leaf [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6), (4, 5), 4]
leaf [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6), (4, 5), 5]
inner [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6), (4, 5)]
leaf [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6), 6]
inner [(((1, 2), (4, 5), 6), (7, 9)), ((1, 2), (4, 5), 6)]
leaf [(((1, 2), (4, 5), 6), (7, 9)), (7, 9), 7]
leaf [(((1, 2), (4, 5), 6), (7, 9)), (7, 9), 9]
inner [(((1, 2), (4, 5), 6), (7, 9)), (7, 9)]
inner [(((1, 2), (4, 5), 6), (7, 9))]
"""