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?
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)
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.
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]
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With