我试图用迭代器实现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
当两个注释行未注释时,它会工作(生成一个具有所需素数的迭代器对象)。否则,它只对每个数字进行迭代
我期待着你的回答:) 谢谢
下面一次又一次地给出了代码中发生的具体情况。为了方便起见,我在第一次迭代中将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
,并计算一个值,即3
(3%2!=0
),即现在的prime
(延迟计算L2
的值,即仅按需计算值。现在yield prime
将产生3
。在第三次迭代中}产生的值是
prime=next(L2)
。现在事情变得有点复杂。要从L2
中得到一个值,需要计算L1
的一个值,要计算一个值L1
,需要计算一个值L
。如果你没记错L
现在将产生4
,现在L1
将使用它产生一个值。但是对prime
的最新引用是3
。4%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更好
相关问题 更多 >
编程相关推荐