26

过滤器类函数接受一个条件(a -> Bool)并在过滤时应用它。

当您有多个条件时,使用过滤器的最佳方法是什么?

使用应用函数 liftA2 而不是 liftM2 因为我出于某种原因不明白 liftM2 在纯代码中是如何工作的。

4

4 回答 4

36

liftM2 组合器可以在 Reader monad 中使用,以“更实用”的方式执行此操作:

import Control.Monad
import Control.Monad.Reader

-- ....

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

请注意,导入很重要;Control.Monad.Reader 提供了使这一切工作的 Monad (e ->) 实例。

之所以可行,是因为对于某些环境 e,reader monad 只是 (e ->)。因此,布尔谓词是在与其参数相对应的环境中返回布尔值的 0 元一元函数。然后我们可以使用 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]
于 2009-05-08T21:38:48.097 回答
20

好吧,您可以在 Haskell 中组合您想要的函数(只要类型正确)并使用 lambda,您甚至不必命名您的谓词函数,即

filter (\x -> odd x && x > 100) [1..200]
于 2009-05-08T21:28:02.697 回答
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]
于 2009-05-08T21:40:14.193 回答
4

如果你有一个类型的过滤函数列表,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。请注意,您不能对任何一个函数进行 η-reduce,因为fnsRHS 上的引用位于匿名函数中。

像这样使用它:

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"
于 2015-01-31T19:13:03.817 回答