【问题标题】:Aggregating values of nodes over a tree在树上聚合节点的值
【发布时间】:2019-12-29 02:44:49
【问题描述】:

我有一个相当复杂的树结构,我想遍历和聚合值(例如求和、连接、追加......)。我希望 traverse 成为树的一种方法,而不是一个单独的函数。我可以遍历并将函数应用于每个节点,但我不知道如何更新与树关联的聚合变量。

这是我的代码的简化版本,包括一个没有聚合的节点处理程序,这是可行的。

# A tree is a node that recursively includes more nodes under it.
# More correctly the Tree is the particular node regarded as the root Node.
class Node:
    def __init__(self, value):
        self.value = value
        self.children = []
    def traverse(self, handle_node):
        handle_node(self)
        for child in self.children:
            child.traverse(handle_node)

tree = Node('A')
tree.children.append(Node('B')) # Note we append Nodes not values!
tree.children.append(Node('C'))
tree.children[1].children.append(Node('D'))
tree.children[1].children.append(Node('E'))

# This is just an example of a node-handler.
def example_func_on_a_node(node):
    print(node.value)

# Execute the node handler on every node in the tree.
tree.traverse(example_func_on_a_node)

正如预期的那样打印出来

A
B
C
D
E

但是,我想要的是聚合在一般情况下。如果我有一个返回值的节点处理程序,我如何将每个节点的值与所有先前节点的值结合起来最终返回聚合。节点处理程序需要是一个通用函数,它接受一个节点作为它的参数并返回一个值。

例如,假设我想连接上述简单示例中的值。代码应该看起来像这样,但这不起作用。

class Node2:
    def __init__(self, value):
        self.value = value
        self.children = []
        self.concat = ''
    def traverse(self, handle_node):
        self.concat += handle_node(self)
        for child in self.children:
            child.traverse(handle_node)

tree = Node2('A')
tree.children.append(Node2('B')) # Note we append Nodes not values!
tree.children.append(Node2('C'))
tree.children[1].children.append(Node2('D'))
tree.children[1].children.append(Node2('E'))

# This is a node-handler that returns a value I want to aggregate.
def example_func_on_a_node2(node):
     return node.value

# Execute the node handler on every node in the tree.
tree.traverse(example_func_on_a_node2)
print(tree.concat)

我得到的答案是A,但我想要的是'ABCDE'

注意我尝试了许多变体,包括使用静态和类方法,以及非局部变量,但很可能使用不正确。

我可以看到我需要什么,但看不到聚合变量的放置位置,因此它在树周围传递并返回根节点,同时保持节点处理程序和节点类简单。

【问题讨论】:

    标签: python tree nodes


    【解决方案1】:

    您的代码不起作用,因为self.concat 指的是当前节点。因此,您不会在某处聚合值,而是将每个节点的值放入单独的 concat 变量中,最终只打印其中一个(第一个)。所以你需要做的是为traverse的每次调用提供对同一个变量的引用。有多种方法可以做到这一点:

    可能性 A:使用全局变量(另见 Why are global variables evil?

    class Node2:
        def __init__(self, value):
            self.value = value
            self.children = []
        def traverse(self, handle_node):
            global concat
            concat += handle_node(self)
            for child in self.children:
                child.traverse(handle_node)
    
    concat = ''
    tree = Node2('A')
    tree.children.append(Node2('B')) # Note we append Nodes not values!
    tree.children.append(Node2('C'))
    tree.children[1].children.append(Node2('D'))
    tree.children[1].children.append(Node2('E'))
    
    # This is a node-handler that returns a value I want to aggregate.
    def example_func_on_a_node2(node):
         return node.value
    
    # Execute the node handler on every node in the tree.
    tree.traverse(example_func_on_a_node2)
    print(concat)
    

    可能性 B:使用类变量

    class Node2:
        concat = ''
        def __init__(self, value):
            self.value = value
            self.children = []
        def traverse(self, handle_node):
            self.__class__.concat += handle_node(self)
            for child in self.children:
                child.traverse(handle_node)
    
    tree = Node2('A')
    tree.children.append(Node2('B')) # Note we append Nodes not values!
    tree.children.append(Node2('C'))
    tree.children[1].children.append(Node2('D'))
    tree.children[1].children.append(Node2('E'))
    
    # This is a node-handler that returns a value I want to aggregate.
    def example_func_on_a_node2(node):
         return node.value
    
    # Execute the node handler on every node in the tree.
    tree.traverse(example_func_on_a_node2)
    print(tree.concat)
    

    可能性 C:使用 traverse 中的 return 将内部 concat 值“中继”到第一个值

    class Node2:
        def __init__(self, value):
            self.value = value
            self.children = []
            self.concat = ''
        def traverse(self, handle_node):
            self.concat += handle_node(self)
            for child in self.children:
                self.concat += child.traverse(handle_node)
            return self.concat
    
    tree = Node2('A')
    tree.children.append(Node2('B')) # Note we append Nodes not values!
    tree.children.append(Node2('C'))
    tree.children[1].children.append(Node2('D'))
    tree.children[1].children.append(Node2('E'))
    
    # This is a node-handler that returns a value I want to aggregate.
    def example_func_on_a_node2(node):
         return node.value
    
    # Execute the node handler on every node in the tree.
    tree.traverse(example_func_on_a_node2)
    print(tree.concat)
    

    可能性 D:向traverse 添加一个可选参数,以将对第一个节点的引用传递给对traverse 的后续调用

    class Node2:
        def __init__(self, value):
            self.value = value
            self.children = []
            self.concat = ''
        def traverse(self, handle_node, tree=None):
            if tree is None:
                tree = self
            tree.concat += handle_node(self)
            for child in self.children:
                child.traverse(handle_node, tree)
    
    tree = Node2('A')
    tree.children.append(Node2('B')) # Note we append Nodes not values!
    tree.children.append(Node2('C'))
    tree.children[1].children.append(Node2('D'))
    tree.children[1].children.append(Node2('E'))
    
    # This is a node-handler that returns a value I want to aggregate.
    def example_func_on_a_node2(node):
         return node.value
    
    # Execute the node handler on every node in the tree.
    tree.traverse(example_func_on_a_node2)
    print(tree.concat)
    

    【讨论】:

    • 谢谢,太棒了,选项 B 最适合我的特定用例,但我也会尝试 C 和 D。对于我的用例 example_func_on_a_node2 可以是 Node2 的方法(未在简化问题中显示)。
    • 实际上选项 C 最适合我的用例,因为在选项 B 下,类变量可能存在争用。选项 C 很好地概括了我的要求,因为我可以将返回的值收集到列表或字典中,然后以任何方式聚合它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    相关资源
    最近更新 更多