2010-09-13 14 views
7

Supongamos que tiene alguna lista L y desea dividirla en dos listas basadas en alguna función booleana P. Es decir, desea una lista de todos los elementos l donde P(l) es verdadero y otra lista donde P(l) es falso.¿Hay una expresión de programación funcional para filtrar una lista en trues y falsos?

puedo implementar esto en Python, así:

def multifilter(pred,seq): 
    trues,falses = [],[] 
    for x in seq: 
     if pred(x): 
      trues.append(x) 
     else: 
      falses.append(x) 
    return trues,falses 

Mi pregunta: ¿Existe un lenguaje de programación funcional que logra esto?

Respuesta

9

De ejemplos itertools:

from itertools import tee, filterfalse 
def partition(pred, iterable): 
    t1, t2 = tee(iterable) 
    return filterfalse(pred, t1), filter(pred, t2) 
+0

Me gusta eso, es más general que lo que hice. – wheaties

+0

esta es probablemente la mejor manera de hacerlo en Python. funciona con generadores también El único defecto es que no es un trazador de líneas = P. Pero podrías hacer eso con un poco de abuso: 'lambda pred, iterable: tuple (f (pred, t) para f, t en zip ([filter, filterfalse], tee (iterable)))'. no es que eso sea ... mejor en cualquier forma – Claudiu

+0

Estaba pensando que no había usado 'itertools.tee' hace bastante poco ... ¡gracias! – perimosocordiae

1

Sure is is. De hecho, ya hay uno encontrado en Python. Consulte itertools para la función groupby. Sin embargo, primero tendrá que ordenar la lista por el tipo de función. Estoy bastante seguro de que esto no es lo que buscas, pero es un comienzo.

Me

, siempre me ha implementado lo que está pidiendo a través de ifilter:

def multifilter(pred, arg): 
    return ifilter(pred, arg), ifilter(lambda x: !pred(x), arg) 
+0

Sí, las ideas 'groupby' y' ifilter' funcionarán, pero me hacen sentir sucio por tomar lo que debería ser una operación de un solo paso y hacerlo más complejo. – perimosocordiae

+0

Técnicamente, esto se romperá si intenta pasar un generador u otro iterador como 'arg', ya que los generadores/iteradores son de paso único. Tendrá que usar 'itertools.tee' para generar dos conjuntos de elementos de un solo generador. – Amber

+0

@Amber Yup, vio la respuesta de Alex B. Si tuviera que modificar mi respuesta, existe la posibilidad de que mi respuesta se vote más alta que la suya. No quiero eso. – wheaties

2

Puede hacer esto con un reducen que genera un resultado tupla de 2 elementos.

reduce(lambda x,y: (x[0]+[y],x[1]) if somefunc(y) else (x[0],x[1]+y), somelist, ([],[])) 

Devuelve un 2-tuple; La primera parte es una lista de elementos que hacen que somefunc() devuelva verdadero, el segundo es el resto.

+0

Aha, me tomó un segundo averiguar qué estaba pasando, pero me gusta. – perimosocordiae

+0

es inteligente, pero bastante ineficiente: crea muchas listas pequeñas y hace muchas listas de concatenación – Claudiu

3

Hoogle es una buena herramienta para esto. Puede ingresar un tipo de función y ver todas las funciones con ese tipo. En este caso necesitamos como entrada: una lista de a, y una función que toma un a y devuelve un booleano, y la salida es un par de listas de a. En la sintaxis de Haskell es (a -> Bool) -> [a] -> ([a], [a]). A partir de ahí vemos que la función relevante es partition. Implementación:

partition p xs == (filter p xs, filter (not . p) xs) 

En Python:

partition = lambda p, xs: (filter(p, xs), filter(lambda f: not p(f), xs)) 

O esto es más rápido, pero un poco más feo porque es asimétrica:

partition = lambda p, xs: (filter(p, xs), [z for z in xs if not p(z)]) 

Esto hace el doble del número de cálculos que necesita, sin embargo, pero creo que tienes que hacer eso si no tienes una mutación.

+0

Tenga en cuenta que OP etiquetó su pregunta Python, por lo que pueden o no buscar una solución en ese idioma. Buena respuesta general sin embargo. – Amber

+0

@Amber: sí, buen punto. Me di cuenta de eso también, así que agregué una traducción de Python. – Claudiu

+0

Wow, no había visto a Hoogle antes. ¡Eso es genial! – perimosocordiae

0
trues = [x for x in seq if pred(x)] 
falses = [x for x in seq if not pred(x)] 

Esa es la altura del estilo de programación funcional, si no quiere más llamadas de función.

Cuestiones relacionadas