Как вы комбинируете условия фильтра - PullRequest
23 голосов
/ 09 мая 2009

Класс функций фильтра принимает условие (a -> Bool) и применяет его при фильтрации.

Как лучше всего использовать фильтр при наличии нескольких условий?

Используется аппликативная функция liftA2 вместо liftM2, потому что я по какой-то причине не понял, как liftM2 работал в чистом коде.

Ответы [ 4 ]

30 голосов
/ 09 мая 2009

Комбинатор liftM2 можно использовать в монаде Reader, чтобы сделать это «более функциональным» способом:

import Control.Monad
import Control.Monad.Reader

-- ....

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

Обратите внимание, что импорт важен; Control.Monad.Reader предоставляет экземпляр Monad (e ->), который заставляет все это работать.

Причина, по которой это работает, в том, что монада читателя просто (e ->) для некоторой среды e. Таким образом, логический предикат является 0-арной монадической функцией, возвращающей bool в среде, соответствующей его аргументу. Затем мы можем использовать liftM2 для распределения среды по двум таким предикатам.

Или, проще говоря, liftM2 будет работать примерно так, когда типы работают:

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

Вы также можете определить новый комбинатор, если хотите иметь возможность их легко связывать и / или не хотите связываться с 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]
15 голосов
/ 09 мая 2009

Ну, вы можете комбинировать функции по своему усмотрению в Haskell (при условии, что типы верны), и используя лямбды, вам даже не нужно называть свою функцию предиката, т.е.

filter (\x -> odd x && x > 100) [1..200]
11 голосов
/ 09 мая 2009

Допустим, ваши условия хранятся в списке под названием conditions. Этот список имеет тип [a -> Bool].

Чтобы применить все условия к значению x, вы можете использовать map:

map ($ x) conditions

Это применяет каждое условие к x и возвращает список Bool. Чтобы свести этот список к одному логическому значению: True, если все элементы имеют значение True, и False в противном случае, вы можете использовать функцию and:

and $ map ($ x) conditions

Теперь у вас есть функция, которая объединяет все условия. Давайте дадим ему имя:

combined_condition x = and $ map ($ x) conditions

Эта функция имеет тип a -> Bool, поэтому мы можем использовать ее при вызове filter:

filter combined_condition [1..10]
3 голосов
/ 31 января 2015

Если у вас есть список функций фильтрации типа a -> Bool и вы хотите объединить их в одну краткую функцию фильтрации того же типа, мы можем написать функции, которые будут выполняться просто. Какая из двух функций, которые вы используете ниже, будет зависеть от требуемого поведения фильтра.

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 вернет true, если какая-либо из функций фильтра вернет true , и false, если все функции фильтра вернут false. allfilt вернет true, если все функции фильтра вернут true, и false, если любая из функций фильтра вернет false . Обратите внимание, что вы не можете η-уменьшить ни одну из функций, поскольку ссылки на fns в RHS находятся внутри анонимных функций.

Используйте это так:

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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...