【问题标题】:Why can't I overwrite my old generator in my double itertion?为什么我不能在我的双重迭代中覆盖我的旧生成器?
【发布时间】:2019-08-27 17:27:32
【问题描述】:

我尝试用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))))

不适用于双重迭代。

【问题讨论】:

  • 这实际上并不能回答您的问题,但是您的双重迭代是解决此问题的效率极低的解决方案。如果你遍历一种动物的可能数量,那么只有两种数量的另一种动物是甚至可以想象的解决方案:一种是正确的头数,另一种是正确的腿数。如果这两个数字相同且有效(不是负数),那么这就是您的解决方案,根本不需要内部循环。
  • 是的,我知道这是一个蛮力解决方案:D 并没有考虑太多,因为我是出于无聊才这样做的。不过在此过程中遇到了一个意想不到的问题。
  • "if next(generator) == StopIteration 好吧,这并不像您期望的那样工作。next 没有返回 StopIteration,它引发 它,所以你必须使用异常处理,但这种方法一开始就很糟糕,因为在循环中使用 next 至少可以说是有问题的。你真的想要做的是:“我能否以某种方式摆脱我的 for 循环中的这些巨大的生成器以提高可读性?”。好吧,在这种情况下,使用函数这几乎总是“如何能我让我的代码更有条理和可读性”。

标签: python python-3.x loops generator


【解决方案1】:

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

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

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

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

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}

但是您可以更进一步,也可以不使用外循环。首先假设所有的头都属于鸡。每只鸡都有一对腿。我们可以很容易地计算出有多少额外的腿被计算在内。每多出一对,就有一个头属于兔子而不是鸡。

def Chinese_puzzle(heads,legs):
    rabbits = legs // 2 - heads
    chickens = heads - rabbits
    return {"Rabbits": rabbits, "Chickens": chickens}

我没有在这里进行任何健全性检查,但您可能只需要检查奇数条腿,或负数的鸡或兔子。我建议在这些情况下引发异常,而不是返回错误消息字符串。

【讨论】:

    【解决方案2】:

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

    您可以将其更改为:

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

    这等效于for 循环,只是它每次都对变量求值。

    【讨论】:

    • 嗯...谢谢。我以为我已经尝试过了,但显然我没有足够注意程序的输出/错误。
    • 行不通next 确实不返回 StopIteration,嗯,它*可以,但这不是它通常的工作方式。
    • 哦,是的,现在我想起来了。我没有费心去找出加薪和回报之间的区别。我猜一个基本上以这种方式工作的 jerry-rig 并不完全是“pythonic”?
    • @juanpa.arrivillaga 我已将其更改为使用try/except 来检测StopIteration
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多