我想通过另一个列表中的谓词来过滤列表。 例如:
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter _ _ [] = []
multifilter _ [] _ = []
multifilter f (x:xs) ys = (filter (f x) ys) ++ (multifilter f xs ys)
使用如下:
prelude> multifilter (==) [1,2,3] [5,3,2]
[2,3]
有没有一种标准方法可以做到这一点?
答案 0 :(得分:11)
您可以使用intersectBy
:
λ> :t intersectBy
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
λ> intersectBy (==) [1,2,3] [5,3,2]
[2,3]
您可以使用hoogle使用类型签名搜索函数并找到它们。
答案 1 :(得分:5)
注意:这个答案实现了问题中单词和示例所表达的规范,而不是那里multifilter
实现给出的不同规范。对于后一种可能性,请参阅gallais' answer。
Sibi's answer显示了你应该如何实际做到这一点。无论如何,考虑如何使用filter
编写函数是有益的。首先,我们可以确定两个相关事实:
multifilter
可以直接表示为filter pred
,以便对pred
进行适当选择。给定一个固定的“谓词列表”,您在多重过滤列表中的元素是否仅在结果中取决于该元素的值。multifilter f xs ys
中,您要过滤的列表为xs
,“谓词列表”为ys
。如果不是这样,你会在你的(选择得当的)例子中得到[3,2]
而不是[2,3]
。所以我们有:
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter f xs ys = filter pred xs
where
pred = undefined -- TODO
我们需要做的就是实施pred
。给定元素x
,如果pred
的某些元素True
为y
,则ys
应生成f x y
。我们可以使用any
:
pred x = any (\y -> f x y) ys
-- Or, with less line noise:
pred x = any (f x) ys
因此,multifilter
变为......
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter f xs ys = filter pred xs
where
pred x = any (f x) ys
-- Or, more compactly:
multifilter :: (a -> a -> Bool) -> [a] -> [a] -> [a]
multifilter f xs ys = filter (\x -> any (f x) ys) xs
...这基本等同于intersectBy
,您可以通过查看intersectBy
的实现来看到。
答案 2 :(得分:4)
第三种选择是使用列表理解:
multifilter rel xs ys = [ x | x <- xs, y <- ys, x `rel` y ]
或者,如果您想要部分申请:
multifilter p xs ys = [ x | x <- xs, let f = p x, y <- ys, f y ]
如果您想使用过滤器,
relate rel xs ys = filter (uncurry rel) $ liftM2 (,) xs ys
(并投入map fst
)
答案 3 :(得分:2)
您接受的答案提供的功能与您在帖子中定义的功能不同:当您保留xs
中的元素时,它会保留ys
中的元素。您可以使用multifilter
的更常规类型来识别此错误:
multifilter :: (a -> b -> Bool) -> [a] -> [b] -> [b]
现在,这可以按照帖子中描述的规范实现,如下所示:
multifilter p xs ys = fmap snd
$ filter (uncurry p)
$ concatMap (\ x -> fmap (x,) ys) xs
如果您不介意按照ys
中的顺序保留值,那么您可以有一个更简单的定义:
multifilter' :: (a -> b -> Bool) -> [a] -> [b] -> [b]
multifilter' p xs = filter (flip any xs . flip p)
答案 4 :(得分:1)
只需使用Hoogle通过签名(a -> a -> Bool) -> [a] -> [a] -> [a]
收益intersectBy
:
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]