为什么我不能在两次迭代中覆盖旧的生成器?

时间:2019-08-27 17:27:32

标签: python python-3.x loops generator

我已经尝试使用here中所述的功能来解决中文拼图(这是最后一个练习)。我的代码包含问题的描述,因此您可以看看它。无论如何,我发现,当我使用两个生成器进行两次迭代时,第二个for循环会耗尽第二个生成器,并且由于生成器的本质,我无法通过删除并重置它来重新启动它。您不能在未将生成器向前推进一步的情况下检查其是否耗尽,然后通过执行以下操作将其重置到先前位置:

generator1 = (bla1 for bla1 in Bla1)
generator2 = (bla2 for bla2 in Bla2)

for bla1 in generator1:
    for bla2 in generator2:
        if next(generator) == StopIteration:
            generator2 = (bla2 for bla2 in Bla2)

我知道,我必须通过再次创建生成器来重新初始化生成器,但是看起来我无法将生成器放入变量中并且无法成功进行两次迭代。显然,必须在每次进入循环时动态创建的“ for i in ...”行中指定生成器。我的问题是,我想在一行上创建一个生成器,将其放入变量中,然后随意重置它,这样我就可以进行两次迭代,因为我使用的某些生成器很长。

'''
Question:

Write a program to solve a classic ancient Chinese puzzle: 
We count 35 heads and 94 legs among the chickens and rabbits in a farm.
How many rabbits and how many chickens do we have?

Strategy: Double iteration. Keep the number of one animal constant and
add more of the other until you either hit 94 legs or overshoot.
'''


def Chinese_puzzle(heads,legs):

    if legs%2 != 0:
        return 'There are no crippled animals on this farm.'

    for rabbits, r_legs in enumerate((leg*4 for leg in range(round(legs/4)))):
        for chickens, c_legs in enumerate((leg*2 for leg in range(round(legs/2)))):
            leggs = r_legs+c_legs
            hedds = rabbits+chickens
            if leggs == legs and hedds == heads:
                return {'Chickens':chickens,'Rabbits':rabbits}
            elif leggs > legs or hedds > heads:
                break

    return 'No permutation of rabbits and chickens exists for this number of legs and heads'


print(Chinese_puzzle(35,94))

我可以以某种方式摆脱for循环内的这些巨大生成器以提高可读性吗?

enumerate((leg*4 for leg in range(round(legs/4))))

就像我说的那样

Rabbits = enumerate((leg*4 for leg in range(round(legs/4))))

不适用于两次迭代。

2 个答案:

答案 0 :(得分:2)

我认为您要的是如何重新构造内部生成器,而不用将创建它的调用放在与for循环相同的行中。我认为最简单的方法是将定义generator2的行移到外部循环中:

generator1 = whatever()  # this could be a long generator expression
for i in generator1:
    generator2 = whatever2()
    for j in generator2:
        ...

您的当前代码由于许多原因无法正常工作。其中next不是return的{​​{1}},它引发了一个例外。但是无论如何,没有理由尝试在内部循环中调用StopIteration,您只需要在外部循环的每次迭代中重新创建耗尽的生成器即可。

但是正如jasonharper评论的那样,您实际上不需要两个嵌套循环来解决此问题。首先,让我们尝试一个循环。您所需要做的就是计算要使给定数量的兔子的总计数有效的鸡和鸡腿的数量(当您遍历该数量时)。然后检查腿是否双头。如果是这样,您已经找到了解决方案。

next

但是您可以更进一步,并且也不需要外部循环。首先假设所有头都属于鸡。每只鸡都有一条腿。我们可以轻松计算出多少条额外的腿。每增加一对,其中一个头就属于兔子而不是鸡。

for rabbits in range(heads+1):
    r_legs = rabbits * 4
    chickens = heads - rabbits
    c_legs = legs - r_legs
    if c_legs == chickens * 2:
        return {"Rabbits": rabbits, "Chickens": chickens}

我在这里不做任何检查,但您可能只需要检查腿的奇数或鸡或兔子的负计数即可。我建议在这种情况下引发异常,而不是返回错误消息字符串。

答案 1 :(得分:1)

for bla2 in generator2:在迭代开始时仅对变量求值一次。迭代器保存在内部临时文件中,它不会每次都重新评估变量。

您可以将其更改为:

while True:
    try:
        bla2 = next(generator2)
    except StopIteration:
        break

这等效于for循环,除了它每次都会评估变量。