使用izip的Python递归生成器中出现意外行为

时间:2016-05-22 02:23:34

标签: python recursion generator subset

我已经编写了一个名为size_subsets的函数,它在传递城市列表(数字)时返回特定大小的所有子集。但是,使用izip()而不是两个for-yield块重新启动函数会完全破坏行为。

下面的第二种方法使用izip()重新设置第一种方法,但由于某种原因,它在顶层没有返回任何内容。任何人都可以帮我弄清楚这是为什么吗?

打印语句显示某些(并非所有)正确的子集确实在size_subsets_broken的递归最底层产生,但即使这些也没有达到最高级别某种原因。

def size_subsets(cities, size, sofar):
    if not size:
        yield sofar
        return
    elif len(cities) < size:
        return
    else:
        curr_city = cities.pop()
        for a in size_subsets(cities[:], size - 1, sofar | {curr_city}):
            yield a
        for b in size_subsets(cities[:], size, sofar):
            yield b


def size_subsets_broken(cities, size, sofar):
    if not size:
        yield sofar
        return
    elif len(cities) < size:
        return
    else:
        curr_city = cities.pop()
        inclusive = size_subsets_broken(cities[:], size - 1, sofar | {curr_city})
        exclusive = size_subsets_broken(cities[:], size, sofar)

        for incl_subset, excl_subset in izip(inclusive, exclusive):
            yield incl_subset
            yield excl_subset


print list(size_subsets([1, 2, 3], 2, set()))           # [set([2, 3]), set([1, 3]), set([1, 2])]
print list(size_subsets_broken([1, 2, 3], 2, set()))    # []

1 个答案:

答案 0 :(得分:1)

我怀疑你误解了izip()的工作原理。当最短输入可迭代用尽时,它就会停止,并且没有理由相信您的inclusiveexclusive总是具有相同的长度。

>>> from itertools import izip
>>> for i in izip(range(10), [6]):
...     print i
(0, 6)

>>> for i in izip(range(10), []):
...     print i

请注意,第一个示例中只有一个输出,而第二个示例中根本没有输出。这都是预期的。

详情

size_subsets_broken()的顶级调用会创建一个生成器 - 迭代器(gi)对象。对list()的调用迫使后者做某事。

它使用参数(忽略inclusiveexclusivegi创建sofar[1, 2], 1 [1, 2], 2个对象。 izip()然后尝试将它们组合在一起。

izip()首先尝试从inclusive获取一个值(最终无法提供任何内容,这就是为什么顶级gi也不会产生任何结果 - 实际上,它甚至都没有试图强制exclusive产生任何东西,因为inclusive为空 - izip()“只有在最短的输入迭代用尽时才会停止”。

回想一下,顶级inclusive的参数是[1, 2], 1。它为gi[1], 0创建[1], 1个对象。 izip()通过[1], 0测试推送if not size分支以产生一个值。因此,izip()继续推动[1], 1分支获取值。

反过来会创建[], 0[], 1 gi个分支。另一层izip()推送其中的第一层以产生一个值(再次通过if not size测试),但第二个分支因if len(cities) < size: return而没有产生任何值。因此,izip()一级向上放弃,而它所属的gi不会产生任何效果。

传播链:在每个级别,izip()至少找到一个空迭代器,因此永远不会输入for ... in izip(...):循环的主体(在任何级别)。

顺便说一下,izip()这不是问题。在此算法中尝试使用izip()是没有意义的。