堆栈filter()调用的奇怪行为

时间:2011-06-07 09:10:04

标签: python filter

所以我从for循环中堆叠的一些过滤器中获得了一些有趣的行为。 我将从演示开始:

>>> x = range(100)
>>> x = filter(lambda n: n % 2 == 0, x)
>>> x = filter(lambda n: n % 3 == 0, x)
>>> list(x)
[0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]

这里我们得到预期的输出。我们在过滤器内的过滤器内有一个范围,过滤条件按我们希望的方式堆叠。现在我的问题来了。
我编写了一个函数来计算数字的相对素数。它看起来像这样:

def relative_primes(num):
    '''Returns a list of relative primes, relative to the given number.'''
    if num == 1:
        return []
    elif is_prime(num):
        return list(range(1, num))
    result = range(1, num)
    for factor in prime_factors(num):
        # Why aren't these filters stacking properly?                           
        result = filter(lambda n: n % factor != 0, result)
    return list(result)

无论出于何种原因,过滤器仅应用于从prime_factors()获取的列表中的LAST因子。例如:

>>> prime_factors(30)  
[2, 3, 5]  
>>> relative_primes(30)  
[1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 28, 29]

我们可以看到没有从列表中删除2或3的倍数。为什么会这样?为什么上面的例子有效,但for循环中的过滤器不起作用?

3 个答案:

答案 0 :(得分:7)

在Python 3.x中,filter()返回生成器而不是列表。因此,仅使用factor的最终值,因为所有三个过滤器都使用相同的factor。您需要稍微修改lambda才能使其正常工作。

result = filter(lambda n, factor=factor: n % factor != 0, result)

答案 1 :(得分:1)

迭代器的评估是懒惰的。所有过滤器仅在语句

中进行评估
return list(result)

到那时,factor的值是最后的主要因素。 lambda函数仅包含对本地名称factor的引用,并将使用在执行时分配给该名称的任何值。

解决此问题的一种方法是在每次迭代中转换为列表。

作为旁注,更容易实现此功能

from fractions import gcd
def relative_primes(n):
    return [i for i in range(1, n) if gcd(n, i) == 1]

修改:如果您的表现不是简单,那么您也可以试试这个:

def relative_primes(n):
    sieve = [1] * n
    for i in range(2, n):
        if not sieve[i] or n % i:
            continue
        sieve[::i] = [0] * (n // i)
    return list(itertools.compress(range(n), sieve))

答案 2 :(得分:1)

如果我理解正确并且如果两个整数没有共同的正因子(除数),那么两个整数是相对素数除外1.使用符号来表示最大公约数,如果gcd,则两个整数a和b是相对素数(a,b)== 1。然后您可以通过以下方式使用fractions模块。

from fractions import gcd

num = 30
relative_primes = filter(lambda x: gcd(x,num) == 1, xrange(1,num))