【发布时间】:2015-04-26 14:43:03
【问题描述】:
假设我有一个要遍历的嵌套数据结构。此数据结构包含 节点,这些节点又可以通过node.get_children_generator() 提供他们的孩子。当然,这些孩子也是node 类型,并且以惰性方式进行评估,即由生成器枚举。为简单起见,让我们假设node 没有孩子,函数get_children_generator 只返回一个空列表/生成器(因此我们不必手动检查它是否为空)。
为了遍历嵌套节点的这种数据结构,简单地迭代链接所有生成器是不是一个好主意?那是在创造链条链条等等?还是会产生过多的开销?
我的想法是这样的:
import itertools as it
def traverse_nodes(start_node):
"""Traverses nodes in breadth first manner.
First returns the start node.
For simplicity we require that
there are no cycles in the data structure,
i.e. we are dealing with a simple tree.
"""
node_queue = iter([start_node])
while True:
try:
next_node = node_queue.next()
yield next_node
# Next get the children
child_gen = next_node.get_children_generator()
# The next code line is the one I am worried about
# is it a good idea to make a chain of chains?
node_queue = it.chain(node_queue, child_gen)
except StopIteration:
# There are no more nodes
break
node_queue = it.chain(node_queue, child_gen) 行是遍历的好方法吗?制作一个链条链条等是个好主意吗?
这样你就有了可以执行的东西,这是一个相当愚蠢的 node 类。生成器有点没用,但假设在现实世界的示例中评估孩子的成本有点高,并且确实需要生成器。
class Node(object):
"""Rather silly example of a nested node.
The children are actually stored in a list,
so the generator is actually not needed.
But simply assume that returning a children
requires a lazy evaluation.
"""
counter = 0 # Counter for node identification
def __init__(self):
self.children = [] # children list
self.node_number = Node.counter # identifies the node
Node.counter += 1
def __repr__(self):
return 'I am node #%d' % self.node_number
def get_children_generator(self):
"""Returns a generator over children"""
return (x for x in self.children)
所以下面的代码sn -p
node0 = Node()
node1 = Node()
node2 = Node()
node3 = Node()
node4 = Node()
node5 = Node()
node6 = Node()
node0.children = [node1, node2]
node1.children = [node6]
node2.children = [node3, node5]
node3.children = [node4]
for node in traverse_nodes(node0):
print(node)
打印
我是节点#0
我是节点 #1
我是节点 #2
我是节点 #6
我是节点 #3
我是节点#5
我是节点 #4
【问题讨论】:
-
为什么不简单的迭代器堆栈/队列?这样你就可以像 DFS/BFS 一样遍历你的结构。
-
所以我有两个嵌套循环?一个在堆栈/队列上,另一个在我从中弹出的生成器上?这比上面的方法更好吗?为什么?
-
它是否真的比你的方法更好是一个品味问题,但可以肯定的是它不会受到你的情况下发生的嵌套调用的影响——你需要遍历所有的链来获得队列的第一个元素。考虑到一个节点有 n 个邻居,那么获取最后一个邻居将花费您通过迭代器的
next()方法的 n 个递归调用。 -
老实说,我不完全明白你的意思。你有没有想过通过
next获取node_queue的第一个元素需要花费O(n) 而不是O(1)?那么它就不再是品味的问题了。除非:>>我喜欢我的排序平方 -
查看我的答案以获得一些解释。
标签: python tree generator itertools