3

I tried to print all the prime number less than 100 with flowing code:

def _odd_iter():
    n=1
    while True:
        n=n+2
        yield n

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

def primes():
    yield 2
    it = _odd_iter()
    while True:
            n=next(it)
            yield n
            it = filter(_not_divisible(n),it)

for n in primes():
    if n<100:
        print(n)
    else:
        break

And it works fine. But after I change the _not_divisible function into lambda, it seems not work:

def _odd_iter():
    n=1
    while True:
        n=n+2
        yield n

def primes():
    yield 2
    it = _odd_iter()
    while True:
            n=next(it)
            yield n
            it = filter(lambda x:x%n>0,it)

for n in primes():
    if n<100:
        print(n)
    else:
        break

The result shows that the filter has dropped nothing.

Why doesn't it works with lambda?

mata
  • 67,110
  • 10
  • 163
  • 162
TJM
  • 709
  • 1
  • 6
  • 11
  • 1
    You are running into capture rules with python. See http://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture-in-python – AnilRedshift Jun 15 '16 at 19:55
  • @AnilRedshift Was hoping someone would find a good existing question for this. Thanks! :) – poke Jun 15 '16 at 19:56

1 Answers1

4

The problem is that the n which is used within the lambda will always refer to the local value. If n changes in the surrounding context, it will also do so within the lambda*. If you want to capture the current value, you can use this instead:

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

*It's probably easier to understand if you think of the context as a dictionary in which local variables are stored and looked up and which always stays the same. Explicitly written that way your primes() function would look like this:

def primes():
    ctx = {}
    yield 2
    ctx['it'] = _odd_iter()
    while True:
            ctx['n'] = next(ctx['it'])
            yield ctx['n']
            ctx['it'] = filter(lambda x: x % ctx['n'] > 0, ctx['it'])

Now it should be clear that when values in ctx change, that changes will also be picked up within the lambda. Basically the same also happens without the ctx dict, and that is what often leads to misunderstandings.

mata
  • 67,110
  • 10
  • 163
  • 162
  • I am sorry I do not get the point of the sentence: If `n` changes, it will also within the lambda. Does it means that although the var name `n` in function `primes` points to a new memory address containing a new value, the var name `n` in the lambda still points to the origin address? So, the `n` in the lambda does not change. – TJM Jun 15 '16 at 20:10
  • @TJM - tried to explain it a little better, hope that helps – mata Jun 16 '16 at 05:18