简体   繁体   English

在python中实现“Eratosthenes筛选”时的麻烦过滤器行为

[英]Troublesome filter behavior when implementing the “Sieve of Eratosthenes” in python

I came up with this Python code for the trial division variant of the "Sieve of Eratosthenes" : 我想出了“Eratosthenes筛选”试验版变体的Python代码:

import itertools

def sieve():
    # begin with all natural numbers above 1
    picker = itertools.count(2)
    while True:
        # take the next available number
        v = next(picker)
        yield v
        # filter from the generator its multiples
        picker = filter(lambda x: x % v != 0, picker)

It doesn't work as I expected. 它没有像我预期的那样工作。

When debugging it I get some behavior I don't understand when filter gets called: the x parameter of the lambda gets a concrete argument which is the next element from the picker generator. 在调试它时,我得到了一些在调用filter时我不明白的行为:lambda的x参数得到一个具体的参数,它是picker生成器的下一个元素。 I don't understand this behavior not even after looking at the documentation of filter . 在查看filter的文档后,我不明白这种行为。

Running 运行

s = sieve()
for i in range(5):
    print(next(s))

I get: 我明白了:

2
3
4
5
6

Instead of 代替

2
3
5
7
11

UPDATE: 更新:

My bug comes from a misunderstanding of how lambdas work in Python, more on that here . 我的错误来自于对lambdas如何在Python中工作的误解,更多关于这一点

Adding an extra parameter to the lambda fixes the issue: 向lambda添加额外的参数可以解决问题:

picker = filter(lambda x, prime = v: x % prime != 0, picker)

I think the problem is because you rely on local variables and the generators you create (using filter() ) are referencing the local variables which are overwritten when the generator goes on iterating. 我认为问题是因为你依赖局部变量而你创建的生成器(使用filter() )引用了当生成器继续迭代时被覆盖的局部变量。

If you use a local function instead, it works fine: 如果您使用本地函数,它可以正常工作:

def sieve():
    def selector(iterator, d):
        for x in iterator:
            if x % d != 0:
                yield x
    picker = itertools.count(2)
    while True:
        # take the next available number
        prime = next(picker)
        yield prime
        # filter from the generator its multiples
        picker = selector(picker, prime)

Trying list(itertools.islice(sieve(), 10)) shows: 试用list(itertools.islice(sieve(), 10))显示:

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

But I really need to point out that this is a purely educational solution to show how things work. 但我真的需要指出,这是一个纯粹的教育解决方案,以显示事情是如何运作的。 I would not propose to use this solution for any productive code. 我不建议将此解决方案用于任何生产性代码。 It builds up a large amount of generators internally which are freed only when you drop the handle of the parent generator. 它在内部构建了大量的生成器,只有在删除父生成器的句柄时才会释放它们。 This is probably a waste of resources and you can create an infinite amount of prime numbers without creating an infinite amount of generators. 这可能是浪费资源,您可以创建无限量的素数而无需创建无限量的生成器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM