[Tutor] Here is newbie doc on how to implement generators
Kent Johnson
kent37 at tds.net
Mon Jul 16 23:45:42 CEST 2007
Dave Kuhlman wrote:
> On Fri, Jul 13, 2007 at 12:39:40PM +1200, John Fouhy wrote:
>> On 13/07/07, Dave Kuhlman <dkuhlman at rexx.com> wrote:
>>> And, I have a question -- If you look at the example of the
>>> iterative (non-recursive) generator (the Doubler class), you will
>>> see that it walks a list, not a tree. That's because I was *not*
>>> able to figure out how to implement a non-recursive tree walk
>>> generator.
>> You should just be able to use a stack -- push the children onto a
>> stack, then pop them off and walk through them.
>>
>> Here's an example, using tuples and lists as a basic tree data type:
>>
>> ####### treetest.py #########
>>
>> TREE = (0, [(1, [(2, [(3, []),
>> (4, []),
>> (5, [(6, []),
>> (7, []),
>> (8, []),
>> (9, []),
>> ]),
>> (10, [(11, []),
>> (12, []),
>> ]),
>> ]),
>> (13, [(14, []),
>> (15, []),
>> (16, [])]),
>> (17, []),
>> (18, []),
>> ]),
>> (19, []),
>> (20, []),
>> ])
>>
>> def walkTree(tree):
>> # stack to hold nodes as we walk through
>> stack = []
>> stack.append(tree)
>>
>> while stack:
>> value, children = stack.pop()
>> for child in reversed(children): # reverse children to get
>> the right order.
>> stack.append(child)
>> yield value
>>
>> if __name__ == '__main__':
>> for value in walkTree(TREE):
>> print value
>>
>> ####### treetest.py #########
>
> John -
>
> That is so cool. I can't believe that it is so simple and elegant.
> I thought that a stack was involved in the solution, but I could
> not figure out how to use it. Thanks.
>
> And, to extend this a bit more, here are two slightly modified
> versions of your solution that implement classes whose
> instances are iterators.
>
>
> #
> # Version #1 -- This version has a next() method, as required by
> # the iterator protocol.
> #
> class Node(object):
> def __init__(self, value='<no value>', children=None):
> self.value = chr(value + 97) * 3
> if children is None:
> children = []
> else:
> self.children = children
> def walk_tree(self):
> # stack to hold nodes as we walk through
> stack = []
> stack.append(self)
> while stack:
> node = stack.pop()
> # reverse children to get the right order.
> stack.extend(reversed(node.children))
> yield node
> def __iter__(self):
> self.iterator = self.walk_tree()
> return self
> def next(self):
> return self.iterator.next()
>
>
> #
> # Version #2 -- This version does not have a next() method, but
> # the iterators returned by __iter__() do have a next().
> #
> class Node(object):
> def __init__(self, value='<no value>', children=None):
> self.value = chr(value + 97) * 3
> if children is None:
> children = []
> else:
> self.children = children
> def walk_tree(self):
> # stack to hold nodes as we walk through
> stack = []
> stack.append(self)
> while stack:
> node = stack.pop()
> # reverse children to get the right order.
> stack.extend(reversed(node.children))
> yield node
> def __iter__(self):
> return self.walk_tree()
I think Version 2 is preferable. Not only is it shorter, but it is
safer. Version 1 has essentially a singleton iterator so any code that
tries to iterate the same node more than once will fail. For example
multi-threaded code, or perhaps during an iteration there could be a
reason to start a new iteration.
Kent
More information about the Tutor
mailing list