【问题标题】:How to use properly recursion and side effects in python如何在python中正确使用递归和副作用
【发布时间】:2013-01-07 15:50:59
【问题描述】:

在树形结构中,我试图找到一个分支的所有叶子。这是我写的:

def leafs_of_branch(node,heads=[]):
    if len(node.children()) == 0:
        heads.append(str(node))
    else:    
        for des in node.children():
           leafs_of_branch(des)
    return heads


leafs_of_branch(node)

我不知道为什么,但对我来说感觉不对。它有效,但我想知道是否有更好的方法来使用递归而不创建 heads 参数。

【问题讨论】:

  • @closevoter -- 虽然这最终会受到那个经典问题中提出的问题的影响,但这不是这个问题的意义所在。
  • ...特别是因为该问题特别要求不使用该参数的方法。

标签: python recursion side-effects


【解决方案1】:

这个

def leafs_of_branch(node,heads=[]):

总是一个坏主意。会更好

def leafs_of_branch(node,heads=None):
    heads = heads or []

否则,您总是对 leafs_of_branch 使用相同的列表。在您的具体情况下,它可能没问题,但迟早您会遇到问题。

我推荐:

def leafs_of_branch(node):
    leafs = []
    for des in node.children():
        leafs.extend(leafs_of_branch(des))
    if len(leafs)==0:
        leafs.append(str(node))
    return leafs

leafs_of_branch(node)

我没有执行if len(node.children()==0,而是在下降到所有(可能为零)孩子之后检查 len(leafs)。因此我只调用了一次 node.children()。

【讨论】:

    【解决方案2】:

    我相信这应该可行:

    def leafs_of_branch(node):
        if len(node.children()) == 0:
           return [str(node)]
        else:
           x = []
           for des in node.children():
               x += leafs_of_branch(des)  #x.extend(leafs_of_branch(des)) would work too :-)
           return x
    

    它不是很漂亮,可能可以再精简一点,但我试图尽可能保持原始代码的形式,以使发生的事情一目了然。

    如果您多次调用原始版本,它实际上将不起作用,因为当您附加到 heads 列表时,该列表实际上会在两次调用之间保存。

    【讨论】:

    • 恐怕这行不通。如果这棵树有十层呢?您没有任何递归,您只返回节点或节点的所有 direct 子节点。
    • 另外,在else 语句中,您将附加所有子节点,无论它们是否实际上是叶节点。
    • @ThorstenKranz -- 谢谢。我只是忘了在else 部分进行递归(呃!)。
    • @ThorstenKranz -- 谢谢你让我诚实 ;-)
    【解决方案3】:

    只要递归进行,IMO 就做对了;您在递归调用中缺少 head 参数。无论如何它工作的原因是其他人所说的,默认参数是全局的并且在调用之间重用。

    如果你想避免递归,在这种情况下你可以使用队列或堆栈和循环:

    def leafs_of_branch(node):
        traverse = [node]
        leafs = []
    
        while traverse:
            node = traverse.pop()
            children = node.children()
    
            if children:
                traverse.extend(children)
            else:
                leafs.append(str(node))
    
        return leafs
    

    【讨论】:

      【解决方案4】:

      您也可以通过这种方式递归地定义一个迭代器。

      def leafs_of_branch(node):
          if len(node.children()) == 0:
              yield str(node)
          else:    
              for des in node.children():
                  for leaf in leafs_of_branch(des):
                      yield leaf
      
      leafs = list(leafs_of_branch(node))
      

      【讨论】:

        【解决方案5】:

        首先,避免使用可变对象(列表、字典等)作为默认值,因为默认值是全局的并且在函数调用之间重复使用:

        def bad_func(val, dest=[]):
            dest.append(val)
            print dest
        
        >>> bad_func(1)
        [1]
        >>> bad_func(2)
        [1, 2]  # surprise!
        

        因此,随后的调用将导致完全出乎意料的事情。

        至于递归问题,我改写成这样:

        from itertools import chain
        
        def leafs_of_branch(node):
            children = node.children()
            if not children:  # better than len(children) == 0
                return (node, )
        
            all_leafs = (leafs_of_branch(child) for child in children)
            return chain(*all_leafs)
        

        【讨论】:

          猜你喜欢
          • 2012-10-02
          • 1970-01-01
          • 1970-01-01
          • 2012-04-20
          • 1970-01-01
          • 2015-09-02
          • 1970-01-01
          • 2019-09-30
          • 1970-01-01
          相关资源
          最近更新 更多