List 具有两个参数的Haskell列表筛选

List 具有两个参数的Haskell列表筛选,list,haskell,filter,List,Haskell,Filter,我必须编写一个函数,它用一个结果为True的参数进行过滤,然后用另一个结果为False的参数进行过滤 我试过这个: selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t] selectUnless fx gx (x:xs) = filter gx (filter fx (x:xs)) 但我需要那个“不是gx”的列表 例如: selectUnless (>= 2) (==2) [1,2,3,4] ==

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

我试过这个:

selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t]
selectUnless fx gx (x:xs) = filter gx (filter fx (x:xs))
但我需要那个“不是gx”的列表

例如:

selectUnless (>= 2) (==2) [1,2,3,4] == [3,4]
selectUnless even odd [1..50] == [2,4..50]
Haskell有一个操作符可以将
True
转换为
False
,反之亦然

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

\x -> not (gx x)
因此,这将对
gx
的结果应用
not
。或者我们可以使用,比如:

或由以下机构提供的更优雅的解决方案:


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

因为
filter f。filter g=filter(\x->fx&&gx)
,我们只需要一些方法来反转
g
。正如威廉所提到的,这是以
而不是
的形式存在的。因此,我们:

selectUnless f g = filter (\x -> f x && not (g x))
如果你想变得更聪明一点,你可以举起
&&

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

selectUnless f g = filter (f <&&> not . g) 
()=liftA2(&&)
infixr 3
选择除非f g=filter(f not.g)

您甚至可能会认为这是一个简洁明了的版本,不需要它自己的名字。

这个无点版本有点毛茸茸的,我更喜欢像
select除非=pre(not.)。过滤(.)
其中
pre=flip(.)
filtered=(`on`filter)
,或者Rein Henrich回答中的部分无点版本
filter(f not.g)
,使用
Monad
约束使其正确短路更方便:
ab=a>=\case True->b;False->pure False
(类似于
)@JonPurdy,就像我说的一样。。。对我自己说。。。
(>)r
应用程序已短路。如果你想让它与任意单子短路,你只需要这样做。好的,我应该澄清一下,这确实会进行短路评估,而不是对其他选择的
m
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 (.)
selectUnless f g = filter (\x -> f x && not (g x))
(<&&>) = liftA2 (&&)
infixr 3 <&&>

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