Haskell multifilter

时间:2015-09-07 05:35:46

标签: haskell

我想通过另一个列表中的谓词来过滤列表。 例如:

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]

有没有一种标准方法可以做到这一点?

5 个答案:

答案 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的某些元素Truey,则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]

找到它

https://www.haskell.org/hoogle/?hoogle=%28a+-%3E+a+-%3E+Bool%29+-%3E+%5Ba%5D+-%3E+%5Ba%5D+-%3E+%5Ba%5D

收益intersectBy

intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]