[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

A Problem with Python's 'yield'



I'm going to pick on Python here, but only because the example code will
be short and sweet. :-) I believe several other implementations of
generators have the same problem.

Python's generator system, used naively, turns an O(N) tree traversal
into an O(N log N) tree traversal:

  class Tree:
      def __init__(self, value, left=None, right=None):
          self.value = value
          self.left = left
          self.right = right

      def in_order(self):
          if self.left is not None:
              for v in self.left.in_order():
                  yield v
          yield self.value
          if self.right is not None:
              for v in self.right.in_order():
                  yield v

  t=Tree(2, Tree(1), Tree(3))
  for v in yield_bug.t.in_order():
      print v

This prints:
  1
  2
  3
  
Unfortunately, this snippet calls 'yield' 5 times, because the leaf
values must be yielded twice on their way back up the tree.

We can shorten the code--and make it run in O(N) time--by adding a new
keyword to replace the "for v in ...: yield v" pattern:

  def in_order(self):
      if self.left is not None:
          yield_all self.left.in_order():
      yield self.value
      if self.right is not None:
          yield_all self.right.in_order():

Interestingly enough, this allows you define notions such as
"tail-recursive generation", and apply the usual bag of
recursion-optimization techniques.

Cheers,
Eric