Oleg Pavliv
Oleg Pavliv

Reputation: 21192

filter map vs list comprehension

Is filter/map equivalent to list comprehension? Suppose I have the following function

def fib_gen():
    a,b = 0,1
    yield 0
    yield 1
    while True:
        a,b = b,a+b
        yield b

Now I can use list comprehension to list fib numbers:

a = fib_gen()
print [a.next() for i in range(int(sys.argv[1]))]

Suppose I want to list only even fib numbers. I would do the following with filter/map:

a = fib_gen()
print filter(even, map(lambda x: a.next(), range(int(sys.argv[1]))))

How can I get the same result with list comprehension?

Upvotes: 8

Views: 2818

Answers (6)

ChessMaster
ChessMaster

Reputation: 549

You can always generate the even fibo numbers only too...

def evenfib():
    """ Generates the even fibonacci numbers """
    a, b = 2, 0
    while True:
        a, b = b, a+4*b
        yield a

Upvotes: 0

Skurmedel
Skurmedel

Reputation: 22179

filter and map converts quite easily into a list comprehension.

Here's a basic example:

[hex(n) for n in range(0, 100) if n > 20]

This is equivalent to:

list(map(hex, filter(lambda x: x > 20, range(0, 100))))

The comprehension is in my opinion more readable. However if the conditions become very advanced, I prefer filter.

So in your case:

[n for n in itertools.islice(fib_gen(), 100) if even(n)]

I have used islice here because the sequence is infinite. But if you use a generator expression it becomes an infinite stream as well:

gen = (n for n in fib_gen() if even(n))

Now you can slice the sequence as well with islice:

print itertools.islice(gen, int(sys.argv[1]))

This avoids the need to use next in the comprehensions themselves. As long as you don't try to evaluate the infinite sequence (as we would if we omitted islice in the list comprehension), we can work with your sequence.

Upvotes: 7

murgatroid99
murgatroid99

Reputation: 20297

You could use the following code:

a = fib_gen()
print [a.next() for i in range(int(sys.argv[1])) if i%3==0]

This is a particular case that works because every third fibbonacci number is even.

Upvotes: 1

Cat Plus Plus
Cat Plus Plus

Reputation: 129984

Is filter/map equivalent to list comprehension?

Yes, map(f, L) is equivalent to [f(x) for x in L]. filter(f, L) is equivalent to [x for x in L if f(x)]. But, since list comprehensions with side effects are generally bad (and here you modify the state of the generator), you can use itertools for a bit cleaner solution:

 a = fib_gen()
 a = itertools.islice(a, int(sys.argv[1]))
 a = itertools.ifilter(even, a)
 print list(a)

Upvotes: 13

kennytm
kennytm

Reputation: 523724

You could use a generator to store the intermediate result, and "filter" on it.

fibs = (a.next() for i in whatever)
even_fibs = [num for num in fibs if num % 2 == 0]

or in one line:

even_fibs = [num for num in (a.next() for i in whatever) if num % 2 == 0]

Note that, if you want to take a definite number of elements from an iterator, you could use itertools.islice instead:

from itertools import islice
fibs_max_count = int(sys.argv[1])
even_fibs = [num for num in islice(fib_gen(), fibs_max_count) if num%2 == 0]

Upvotes: 10

badzil
badzil

Reputation: 3610

I don't thing this is going to work with a generator. In order to have this work with list comprehension, you would need to have:

print [a.next() for i in range(int(sys.argv[1])) if even(a.next())]

This returns:

[1, 3, 13, 55, 233]

The problem is you need to access twice the next number in the list but the second a.next() call makes what it supposed to do, ie get the next number. To my knowledge, it is not possible to store the value of a.next() with list comprehension to use twice.

Upvotes: 1

Related Questions