2009-05-08 17 views
23

Klasa filtru funkcji przyjmuje warunek (a -> Bool) i stosuje go podczas filtrowania.W jaki sposób można łączyć warunki filtrowania?

Jaki jest najlepszy sposób użycia filtra w przypadku wielu warunków?

Zastosowano funkcję aplikacyjną liftA2 zamiast liftM2, ponieważ z jakiegoś powodu nie rozumiałem, jak liftM2 działało w ramach czystego kodu.

Odpowiedz

29

liftM2 kombinator może być użyty w Monadzie Czytnika, aby zrobić to w "bardziej funkcjonalny" sposób:

import Control.Monad 
import Control.Monad.Reader 

-- .... 

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

Należy pamiętać, że import jest ważny; Control.Monad.Reader zapewnia instancję Monad (e ->), która sprawia, że ​​wszystko działa.

Powodem tego jest monada czytelnika po prostu (e ->) dla jakiegoś środowiska e. Zatem predykat boolowski jest 0-funkcyjną funkcją monadyczną zwracającą bool w środowisku odpowiadającym jej argumentowi. Następnie możemy użyć liftM2, aby rozdzielić środowisko na dwa takie predykaty.

Lub w prostszych słowach, liftM2 będzie działać trochę jak ten, gdy typy wypracowanie:

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

Można również zdefiniować nowy COMBINATOR jeśli chcesz być w stanie łańcucha nich łatwo i/lub nie chcą zadzierać z 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

Oba przykłady pracy na GHC 7.6.3 nawet jeśli nie importować 'Control.Monad.Reader'. – sjakobi

+2

Jestem 'liftM2' może (obecnie) być zastąpiony w następujący sposób:' filtr ((&&) <$> nieparzysty <*> (> 100)) [1.200] '. Co jest takie samo, ale ładniejsze. :) Wymaga również tylko 'Control.Applicative', i nie ma pełnych monad. ... Chociaż wciąż zastanawiam się, który operator zezwala na ANDing więcej niż dwie funkcje Boolean ... – Evi1M4chine

15

Cóż, można połączyć funkcje jednak chcesz w Haskell (tak długo, jak typy są poprawne) i za pomocą lambdy nie trzeba nawet nazwać funkcję źródłowe, tj

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

Powiedzmy wasze warunki są przechowywane na liście zwanej conditions. Ta lista ma typ [a -> Bool].

Aby zastosować wszystkie warunki do wartości x, można użyć map:

map ($ x) conditions 

Dotyczy to każdy warunek x i zwraca listę Bool. Aby zmniejszyć tę listę w jednym boolean, czy wszystkie elementy są prawda i fałsz w przeciwnym razie, można użyć funkcji and:

and $ map ($ x) conditions 

Teraz masz funkcję, która łączy w sobie wszystkie warunki. Dajmy mu nazwę:

combined_condition x = and $ map ($ x) conditions 

Ta funkcja ma typ a -> Bool, więc możemy użyć go w wywołaniu filter:

filter combined_condition [1..10] 
+2

Ostrożnie z ($ x) w przeciwieństwie do ($ x), tak jakbyś włączał szablon Haskella z jakiegoś innego powodu, a $ x nagle wygląda jak splot . –

+8

Odkryłeś funkcję "wszystko": 'filtr (wszystkie warunki) [1..10]' –

+1

Istnieje również dowolna funkcja, w zależności od tego, jak chcesz łączyć predykaty: dowolne p = lub. mapa p; wszystkie p = i. mapa p; –

2

Jeśli masz listę funkcji filtrowania typu a -> Bool i chcesz połączyć je w jedną zwięzłą funkcję filtrowania tego samego typu, możemy pisać funkcje do zrobienia właśnie. Która z poniższych funkcji będzie zależeć od zachowania filtra, którego potrzebujesz.

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 powróci prawdziwą, jeśli którykolwiek z funkcji filtrujących return true i false jeśli wszystkich funkcji filtrujących return false. allfilt zwróci wartość true, jeśli wszystkie funkcje filtru zwrócą true i fałsz, jeśli którakolwiek z funkcji filtru zwróci wartość false. Należy zauważyć, że nie można η-zmniejszyć żadnej z funkcji, ponieważ odniesienia do fns na RHS mają anonimowe funkcje.

Używaj go tak:

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" 
Powiązane problemy