【问题标题】:Strange behaviour Python generator with recursivity具有递归性的奇怪行为 Python 生成器
【发布时间】:2017-11-18 14:01:47
【问题描述】:

我最近研究了一种算法,用于在 Sagemath 的有向图中列出生成树。

这样做,我和我的同事发现了一种我们并不完全理解的奇怪行为。

在规范方面,Sagemath 使用 Python 2.7 和 import absolute import from __future__

该算法使用“yield”关键字一一列出生成树,并对其自身进行递归调用。 在某一时刻,我们会在递归调用之前进行测试,以确保它不会触发错误。

但是,如果我们删除测试并让算法生成错误而不捕获它,生成器不会自行停止而是传递到下一个递归调用。

如果我可以给出一个图解的解释,假设每个递归调用有 3 个步骤,我们得到了这样的结果:

  • 0 级 - 步骤 1
  • 0 级 - 步骤 2
    • 1 级 - 步骤 1
    • 级别 1 - 步骤 2
      • level 2 - step 1
    • 级别 1 - 步骤 3
      • 第 2 级 - 第 1 步
      • 2 级 - 产量 -level 0 - 第 3 步
  • ...

如果我们使用“try catch”捕获错误,那么算法将自行停止。

我把代码放在这里;如果您不了解 Sagemath,我不确定您是否能掌握所有内容,但其中大部分是可以理解的。我会做出具体的评论以突出显示有趣的部分并删除代码的某些部分。

我希望有人可以向我解释这种行为;就像生成器对象可以在递归调用期间处理异常并在生成时忽略它们。

def _rec_spanning_trees():
        if len(list_merged_edges) == self.order()-1: # CONDITION TO YIELD
            for indexes in product(*list_merged_edges):
                yield DiGraph([list_edges[index] for index in indexes], format='list_of_edges', pos=self.get_pos())

        # part removed

        # THIS HERE !
        # "outgoing_edge_iterator" is raising an error if D does not have any edges
        s, x, l = D.outgoing_edge_iterator(source).next()

        # HERE ! 
        # If I remove the "if(len(...." then the line above will raise an error sometime, but do not if I let it
        D.delete_edge(s, x, l)
        if len(list(D.depth_first_search(source))) == D.order():
           for tree in _rec_spanning_trees():
               yield tree
        D.add_edge(s, x, l)

        # part removed

        for tree in _rec_spanning_trees():
            yield tree

        # part removed

【问题讨论】:

  • 向我们展示完整、准确的错误信息。
  • + 你的代码是如何被调用的——所以我们将能够重现
  • @user2357112 这就是重点,没有错误,生成器似乎可以处理它。
  • @alfasin 这段代码是一个类方法的实习函数,但 user2357112 给出的答案似乎相当不错

标签: python recursion generator


【解决方案1】:

D.outgoing_edge_iterator(source).next() 肯定会产生异常,但该异常是 StopIteration。这是用于表示迭代器结束的异常,当它从生成器传播到上面的 for 循环时,for 循环将其视为循环已完成。

此行为在最近的 Python 版本中为 changed;在生成器中引发的 StopIteration 现在将转换为 RuntimeError

【讨论】:

  • 哦!好的 !那么可以假设它会保持这种状态吗?这是一个很好的方法吗?去掉这个“if”会使 all 算法更快
  • @XanX3601:我看不到你的整个算法,但在我看来,删除条件会使你的算法出错;看起来如果您删除该条件,您的算法可能会开始生成断开连接的子图,而不仅仅是生成树。
  • Sage 已经包含一个spanning tree generation function,所以你可以使用它。如果您特别需要一个迭代器(Sage 的函数会生成一个列表),那么将您的代码基于 Sage 的代码可能是一个很好的方法。
  • 我们的测试表明无论有没有它都可以工作。代码更易于阅读,但我们执行操作的顺序防止了断开的子图。因此,现在更多的是关于性能的清晰度问题。我会把你的答案转达给和我一起工作的人。非常感谢!
  • 是的,Sagemath 确实有一种方法,但仅适用于无向图,我们需要一种用于有向方法的有向图,它的方法较少,比无向图使用较少
猜你喜欢
  • 2020-02-29
  • 2016-08-05
  • 2020-11-11
  • 1970-01-01
  • 2013-05-19
  • 2016-11-06
  • 2016-11-07
  • 2015-01-31
  • 1970-01-01
相关资源
最近更新 更多