Haskell列表过滤带有两个参数

时间:2019-05-22 20:08:59

标签: list haskell filter

我必须编写一个函数,该函数使用一个结果为True的参数进行过滤,然后使用另一个结果为False的参数进行过滤

我尝试过:

selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t]
selectUnless fx gx (x:xs) = filter gx (filter fx (x:xs))

但是我需要“ not gx”所在的列表。

例如:

selectUnless (>= 2) (==2) [1,2,3,4] == [3,4]
selectUnless even odd [1..50] == [2,4..50]

2 个答案:

答案 0 :(得分:5)

Haskell有一个not :: Bool -> Bool运算符,可将True转换为False,反之亦然。

问题当然是gx不是Bool,而是函数t -> Bool。但是,我们可以构造如下函数:

\x -> not (gx x)

因此,这会将not应用于gx x的结果。或者我们可以使用(.) :: (b -> c) -> (a -> b) -> a -> c,例如:

selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t]
selectUnless fx gx = filter (not . gx) . filter fx

通过使用liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c,我们甚至可以做到完全没有意义:

import Control.Applicative(liftA2)

selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t]
selectUnless = (filter .) . (. (not .)) . liftA2 (&&)

@JonPurdy提供的更优雅的解决方案:

import Control.Applicative(liftA2)
import Data.Function(on)

pre :: (a -> b) -> (b -> c) -> a -> c
pre = flip (.)

filtered :: (([a] -> [a]) -> ([a] -> [a]) -> c) -> (a -> Bool) -> (a -> Bool) -> c
filtered = (`on` filter)

selectUnless :: (a -> Bool) -> (a -> Bool) -> [a] -> [a]
selectUnless = pre (not .) . filtered (.)

请注意,您的原始代码无法处理空列表:实际上,您仅在列表的函数中指定了(x:xs)模式,而不是[]。但是,这里没有必要使用额外的模式,filter已经可以处理空列表和非空列表。

答案 1 :(得分:4)

由于filter f . filter g = filter (\x -> f x && g x),我们只需要某种方法即可反转g。正如Willem所提到的,它以not的形式存在。所以我们有:

selectUnless f g = filter (\x -> f x && not (g x))

如果您想变得更聪明,可以抬起&&

(<&&>) = liftA2 (&&)
infixr 3 <&&>

selectUnless f g = filter (f <&&> not . g) 

您甚至可能会认为这足够简洁,意图公开,不需要自己的名字。