2011-06-19 7 views
6

¿El filtro/mapa es equivalente a la comprensión de la lista? Supongamos que tengo la siguiente funciónmapa de filtro frente a la lista de comprensión

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

Ahora puedo utilizar comprensión de lista para enumerar los números fib:

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

Supongamos que quiero hacer una lista única, incluso fib números. Haría lo siguiente con filtro/mapa:

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

¿Cómo puedo obtener el mismo resultado con la lista de comprensión?

Respuesta

9

Puede usar un generador para almacenar el resultado intermedio y "filtrar" en él.

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

o en una línea:

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

Tenga en cuenta que, si usted quiere tomar un número determinado de elementos de un iterador, se puede usar itertools.islice lugar:

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] 
6

filter y map se convierte bastante fácilmente en una lista de comprensión.

Aquí hay un ejemplo básico:

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

Esto es equivalente a:

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

La comprensión es en mi opinión más legible. Sin embargo, si las condiciones se vuelven muy avanzadas, prefiero filter.

Así, en su caso:

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

he utilizado islice aquí porque la secuencia es infinita. Pero si se utiliza una expresión del generador se convierte en un flujo infinito, así:

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

ya se puede cortar la secuencia, así como con islice:

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

Esto evita la necesidad de utilizar next en las comprensiones sí mismos. Siempre y cuando no intente evaluar la secuencia infinita (como lo haríamos si omitiéramos islice en la lista de comprensión), podemos trabajar con su secuencia.

+0

Gracias. ¿Qué pasa con las expresiones de filtrado que contienen next() como en mi ejemplo? –

+0

@Oleg: Ah, estoy agregando algo de información, reviso mi última adición y verás que puedes evitar 'next' también. – Skurmedel

1

No creo que esto funcione con un generador. Con el fin de tener este trabajo con la lista de comprensión, lo que tendría que tener:

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

Esto devuelve:

[1, 3, 13, 55, 233] 

El problema es que necesita acceder a dos veces el número siguiente en la lista, pero la segunda a.La llamada siguiente() hace lo que se supone que debe hacer, es decir, obtener el siguiente número. Que yo sepa, no es posible almacenar el valor de a.next() con comprensión de lista para usar dos veces.

+0

es posible pero debe anidar como en 'imprimir [n para n, i en ((a.next(), i) para i en rango (int (sys.argv [1]))) si even (n)] ',' n' es 'a.next()' y se usa dos veces. –

13

¿El filtro/mapa es equivalente a la comprensión de la lista?

Sí, map(f, L) es equivalente a [f(x) for x in L]. filter(f, L) es equivalente a [x for x in L if f(x)]. Pero, dado que las listas por comprensión con efectos secundarios son generalmente mal (y aquí se modifican el estado del generador), puede utilizar itertools para la solución un poco más limpio:

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

Se podría utilizar el siguiente código:

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

Este es un caso particular que funciona porque cada tercer número de fibbonacci es par.

0

Siempre se puede generar el Fibo incluso números demasiado ...

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