迭代器的意外行为

2024-05-18 23:41:40 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用迭代器实现Eratosthenes的筛选(因为我想更多地使用python进行函数编程)。不幸的是,发生了一些意想不到的行为。你可以在这个视频中看到:https://imgur.com/gallery/XfXFw4a

这是我的密码:

def sieve_primes(stop=10):
    L = (x for x in range(2, stop+1))
    while True:
        prime = next(L)
        L = filter(lambda x: x % prime != 0 or x == prime, L)
        #L, M = itertools.tee(L)
        #print(list(M))
        yield prime

当两个注释行未注释时,它会工作(生成一个具有所需素数的迭代器对象)。否则,它只对每个数字进行迭代

我期待着你的回答:) 谢谢


Tags: 函数httpscom密码视频def编程gallery
3条回答
def sieve_primes(stop=10):
    L = (x for x in range(2, stop+1))
    while True:
        prime = next(L)
        L = filter(lambda x: x % prime != 0 or x == prime, L)
        yield prime

下面一次又一次地给出了代码中发生的具体情况。为了方便起见,我在第一次迭代中将L表示为L1,在第二次迭代中将L表示为L2,依此类推

  • 在第一次迭代中prime=next(L)为2(如预期)。 L1=filter(lambda x: x % prime != 0 or x == prime, L)(延迟计算L的值,即仅按需计算。yield prime将产生2预期值。

  • 在第二次迭代中prime=next(L1)。棘手的部分来了。L1是只根据需要计算值的filter object。因此,在第二次迭代中,当执行prime=next(L1)时,只从L计算一个值。现在lambda使用素数作为2,并计算一个值,即33%2!=0),即现在的prime(延迟计算L2的值,即仅按需计算值。现在yield prime将产生3

  • 在第三次迭代中prime=next(L2)。现在事情变得有点复杂。要从L2中得到一个值,需要计算L1的一个值,要计算一个值L1,需要计算一个值L。如果你没记错L现在将产生4,现在L1将使用它产生一个值。但是对prime的最新引用是34%3!=0被计算为True。因此,L1产生4。因此,计算L2{}产生的值是True所以prime=next(L2)产生的值是4

在进一步的迭代中应用相同的逻辑,你会发现5,6,7,8,9…将在进一步的迭代中产生

您正在lambda中使用变量prime,该变量是从封闭范围继承的引用。当您的代码计算lambda时,它将在继承引用的范围内使用绑定到该引用的任何值。当您不使用tee并计算列表时,所有lambda函数都是相同的,并且对prime使用相同的值

tee的工作原理是将结果存储在一个列表中,并在稍后再次询问时从该列表中提供给您,因此对于prime的每个值,它实际上将过滤器应用于L中的所有值

您可以通过在lambda的作用域中绑定prime来解决这个问题,方法是将它作为一个带有默认值的参数传递。这会将该值保存为函数对象的一部分,然后引用prime是对该存储值的本地引用

下面呢?通常使用生成器表达式比使用map/filter/reduce更好

#!/usr/bin/env python3


def sieve_primes(stop=100):
    primes = []
    for candidate in range(2, stop+1):
        if not any(candidate % prime == 0 for prime in primes):
            primes.append(candidate)
            yield candidate


for prime in sieve_primes():
    print(prime)

相关问题 更多 >

    热门问题