过滤器类函数采用条件(a - > Bool)并在过滤时应用它。
当您有多个条件时,使用过滤器的最佳方法是什么?
使用了应用功能 liftA2而不是liftM2,因为我出于某种原因不明白liftM2如何在纯代码中工作。
答案 0 :(得分:30)
liftM2组合器可以在Reader monad中以“更实用”的方式使用:
import Control.Monad
import Control.Monad.Reader
-- ....
filter (liftM2 (&&) odd (> 100)) [1..200]
请注意,进口很重要; Control.Monad.Reader提供Monad(e - >)实例,使其全部工作。
这个工作的原因是读者monad只是(e - >)对于某些环境e。因此,布尔谓词是0-ary monadic函数,在与其参数对应的环境中返回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]
答案 1 :(得分:15)
好吧,你可以在Haskell中组合你想要的函数(只要类型是正确的)并使用lambdas你甚至不必命名你的谓词函数,即
filter (\x -> odd x && x > 100) [1..200]
答案 2 :(得分:11)
假设您的条件存储在名为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 :(得分:3)
如果你有一个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。
如果所有过滤器函数都返回true,则allfilt
将返回true;如果任何过滤器函数返回false ,则{strong> false将返回false。
请注意,您不能η-减少任何一个函数,因为RHS上fns
的引用是在匿名函数内。
像这样使用:
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"