traverse.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. """
  2. Tree traversal routines.
  3. Created by Sander Mathijs Van Veen <smvv@kompiler.org>, 2012.
  4. """
  5. from node import Leaf
  6. def traverse_depth_first(root):
  7. """
  8. Traverse the given root of a tree in depth-first order. This is an
  9. iterative instead of the ordinary recursive implementation. It uses
  10. iterators to reduce memory usage and, since it is iterative, the depth of
  11. the given tree does not hit Python's recursion limit.
  12. This implementation uses two stacks. One contains the path from the root
  13. node to the current visited node / leaf using child node indices. The other
  14. one contains pointers to the nodes which are not yet visited (aka still on
  15. the "to do" list). Thus, traversal using this implementation has a worst
  16. case space complexity of O(2 * log N), where N is the number of nodes in
  17. the tree.
  18. >>> from node import Node as N, Leaf as L
  19. >>> root = N('+', N('/', L(1), L(2)), N('*', L(3), L(4)))
  20. >>> print map(lambda n: n.title(), traverse_depth_first(root))
  21. ['1', '2', '/', '3', '4', '*', '+']
  22. >>> root = N('+', N('/', L(1), N('-', L(2), N('^', L(3), L(4)))), \
  23. N('*', L(5), L(6)))
  24. >>> print map(lambda n: n.title(), traverse_depth_first(root))
  25. ['1', '2', '3', '4', '^', '-', '/', '5', '6', '*', '+']
  26. """
  27. if not root:
  28. raise StopIteration()
  29. # List of nodes which are not yet visited (the "to do" list).
  30. stack = []
  31. # List of child node indices which indicates the path to the current
  32. # visited node or leaf.
  33. path = []
  34. node = root
  35. while True:
  36. while not isinstance(node, Leaf):
  37. # Traverse left and save the path
  38. stack.append(node)
  39. path.append(0)
  40. node = node[0]
  41. yield node
  42. if not stack:
  43. break
  44. while stack:
  45. # Set pointer to parent node and increase child node position.
  46. node = stack[-1]
  47. path[-1] += 1
  48. pos = path[-1]
  49. if pos >= len(node):
  50. # Visit the parent node, since all children are visited.
  51. yield node
  52. # Remove the node and move up in the tree.
  53. stack.pop()
  54. path.pop()
  55. if not stack:
  56. raise StopIteration()
  57. else:
  58. # This child node is not visited.
  59. node = node[pos]
  60. break
  61. raise StopIteration()
  62. if __name__ == '__main__':
  63. import doctest
  64. doctest.testmod()