B.Law
B.Law

Reputation: 117

Problems about filter and lambda

I wrote some codes in python to get some primes:

N = (x for x in range(2,100))

while i<50:
    n = next(N)
    print(n)
    N = filter(lambda x:x % n > 0,N)
    i = i+1

I think it should print primes 2,3,5,7,11.... But it turns out to be 2,3,4,5,6,7... Just like the filter didn't work. I guess maybe it's a problems of lambda, which didn't provide the value of n successfully, so I just change my codes to these:

def fil(n):
    return lambda x:x % n > 0

N = (x for x in range(2,100))

i = 0
while i<50:
    n = next(N)
    print(n)
    N = filter(fil(n),N)
    i = i+1

It works.

But I still doubt that, so I wrote these:

N = (x for x in range(2,100))

i = 0
while i<50:
    n = next(N)
    print(n)
    N = filter(lambda x:x % n == 0,N)
    i = i+1

Just changed the lambda x:x % n > 0 to lambda x:x % n==0. Other parts are identical. And this time it works, gives me 2^x: 2,4,8,16,32... The filter works.

It really confused me. How to explain / understand this?

Upvotes: 1

Views: 80

Answers (1)

ShadowRanger
ShadowRanger

Reputation: 155744

filter on Python 3 is lazy, so the filter isn't applied until the next number is requested. When you don't use closure scope for the predicate, you end up using the live value of n, so you're effectively testing if each number is divisible by the previous number (that is, when testing 4, each filter wrapping is rechecking whether it's divisible by 3, 5 is checked for divisibility by 4 over and over, etc.), which is never the case (I strongly suspect your output never included 1 though, since your original genexpr doesn't even produce 1).

The simplest fix to your original code is to use a default argument to bind n at function definition time, rather than referencing it in the body of the function, which looks it up in nested scope at call time, getting a live value. All you have to do is change:

N = filter(lambda x: x % n > 0, N)

to:

N = filter(lambda x, n=n: x % n > 0, N)

Upvotes: 2

Related Questions