2009-05-08 13 views
23

La clase de filtro de funciones toma una condición (a -> Bool) y la aplica al filtrar.Cómo se combinan las condiciones de filtro

¿Cuál es la mejor manera de utilizar un filtro cuando tiene múltiples condiciones?

Se utilizó la función de aplicación liftA2 en lugar de liftM2 porque, por algún motivo, no entendía cómo funcionaba liftM2 dentro del código puro.

Respuesta

29

El liftM2 combinador puede ser utilizado en la mónada lector a hacer esto de una manera 'más funcional':

import Control.Monad 
import Control.Monad.Reader 

-- .... 

filter (liftM2 (&&) odd (> 100)) [1..200] 

Tenga en cuenta que las importaciones son importantes; Control.Monad.Reader proporciona la instancia de Monad (e ->) que hace que todo esto funcione.

El motivo por el que esto funciona es que la mónada del lector es justa (e ->) para algún entorno e. Por lo tanto, un predicado booleano es una función monádica 0-ary que devuelve bool en un entorno correspondiente a su argumento. Podemos usar liftM2 para distribuir el entorno sobre dos predicados.

O, en términos más simples, liftM2 actuará un poco como esto cuando los tipos funcionan:

liftM2 f g h a = f (g a) (h a) 

También puede definir un nuevo combinador si desea ser capaz de cadena de éstos con facilidad, y/o no quieren meterse con liftM2:

(.&&.) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool) 
(.&&.) f g a = (f a) && (g a) 
-- or, in points-free style: 
(.&&.) = liftM2 (&&)  

filter (odd .&&. (> 5) .&&. (< 20)) [1..100] 
+3

Ambos ejemplos trabajan en GHC 7.6.3, incluso si no importar 'Control.Monad.Reader'. – sjakobi

+2

Soy 'liftM2' puede (hoy en día) ser reemplazado así:' filter ((&&) <$> odd <*> (> 100)) [1.200] '. Lo cual es lo mismo, pero más bonito. :) También solo requiere 'Control.Applicative', y no hay mónadas completas. ... Aunque todavía me pregunto qué operador permite ANDing más de dos funciones booleanas ... – Evi1M4chine

15

Bueno, puede combinar las funciones que le apetezca en Haskell (siempre y cuando los tipos son correctos) y el uso de lambdas que ni siquiera tiene el nombre de su función de predicado, es decir,

filter (\x -> odd x && x > 100) [1..200] 
9

Digamos que sus condiciones se almacenan en una lista llamada conditions. Esta lista tiene el tipo [a -> Bool].

Para aplicar todas las condiciones para un valor x, puede utilizar map:

map ($ x) conditions 

Esto se aplica a cada condición x y devuelve una lista de Bool. Para reducir esta lista en una única booleano, verdadero si todos los elementos son True y False en caso contrario, se puede utilizar la función and:

and $ map ($ x) conditions 

Ahora usted tienen una función que combina todas las condiciones. Vamos a darle un nombre:

combined_condition x = and $ map ($ x) conditions 

Esta función tiene el tipo a -> Bool, por lo que podemos utilizarlo en una llamada a filter:

filter combined_condition [1..10] 
+2

Cuidado con ($ x) en comparación con ($ x), como si activara Template Haskell por alguna otra razón, el $ x de repente se verá como un empalme . –

+8

Ha descubierto la función 'todo':' filtro (todas las condiciones) [1..10] ' –

+1

También existe la función any, dependiendo de cómo quiera combinar los predicados: any p = or. mapa p; todos p = y. mapa p; –

2

Si usted tiene una lista de funciones de filtrado de tipo a -> Bool y desea combinarlos en una función de filtrado conciso del mismo tipo, podemos escribir funciones para hacer solo. ¿Cuál de las dos funciones que se detallan a continuación dependerá del comportamiento del filtro que necesite?

anyfilt :: [(a -> Bool)] -> (a -> Bool) 
anyfilt fns = \el -> any (\fn -> fn el) fns 

allfilt :: [(a -> Bool)] -> (a -> Bool) 
allfilt fns = \el -> all (\fn -> fn el) fns 

anyfilt volverá cierto si cualquiera de las funciones de filtro de retorno verdadero y falso si todas las funciones de filtro de retorno falsa. allfilt devolverá verdadero si todas las funciones de filtro devuelven verdadero y falso si cualquiera de las funciones de filtro devuelve falso. Tenga en cuenta que no puede η-reducir ninguna función, ya que las referencias a fns en el RHS están dentro de las funciones anónimas.

utilizar de esta manera:

filterLines :: [String] -> [String] 
filterLines = let 
    isComment = isPrefixOf "# " 
    isBlank = (==) "" 
    badLine = anyfilt([isComment, isBlank]) 
    in filter (not . badLine) 

main = mapM_ putStrLn $ filterLines ["# comment", "", "true line"] 
--> "true line" 
Cuestiones relacionadas