Haskell 哈斯克尔多重过滤器

Haskell 哈斯克尔多重过滤器,haskell,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 (==) [

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

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]

有标准的方法吗?

您可以使用
intersectBy

λ> :t intersectBy
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
λ> intersectBy (==) [1,2,3] [5,3,2]
[2,3]
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]

您可以使用来搜索使用类型签名的函数并找到它们。

只需使用Hoogle通过签名
(a->a->Bool)->[a]->[a]->[a]>[a]

产生相交的

λ> :t intersectBy
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
λ> intersectBy (==) [1,2,3] [5,3,2]
[2,3]
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
注意:此答案实现了问题中的单词和示例所表示的规范,而不是
multifilter
实现中给出的不同规范。有关后一种可能性,请参阅

展示了你应该如何去做。在任何情况下,考虑如何使用<代码>过滤器> />代码来编写函数是很有启发性的。首先,我们可以确定两个事实:

  • multifilter
    可以直接表示为
    filter pred
    ,以进行适当的
    pred
    选择。给定一个固定的“谓词列表”,要进行多重筛选的列表元素是否会出现在结果中只取决于该元素的值
  • 多重过滤器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
如果对于
ys
的某个元素
y
fxy
为True。我们可以方便地用
任意
表示:

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
的实现可以看出。

第三个选项是使用列表理解:

multifilter rel xs ys = [ x | x <- xs, y <- ys, x `rel` y ]

(再加上
map fst

您接受的答案提供了一个不同于您在帖子中定义的功能:当您保留
ys
中的元素时,它保留
xs
中的元素。您可以通过为
多重过滤器使用更通用的类型来发现此错误:

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)

@当然,最终的选择是你们的,但我觉得西比的答案更值得接受。库中的一个易于使用的函数完全满足您的需求,总体而言,它是一个比手工编写的更好的解决方案(即使编写自己的函数对学习很有用)。除此之外,
intersectBy
在看到他的答案之前我没有想到。
multifilter f xs ys=filter(\x->any(f x)ys)xs
保留了
xs
中的值,而原始问题保留了
ys
中的值。解决更一般的
multifilter::(a->b->Bool)->[a]->[b]->[b]
将有助于发现这个错误。@gallais我遵循问题中的单词和示例,而不是其中的实现。不一致性(不可否认,我本人和其他使用
intersectBy
的回答者都没有发现)已经存在于问题中。既然如此,我不会改变这个答案,而是链接到你的答案,因为它提出了一个相关的观点。另请参阅我对您答案的评论。@dos顺便说一句,一个简单的测试说明了gallais和我正在讨论的问题是
multifilter((==)(2*)[2,3,12,1][5,3,2,6]
。在您问题中的四个实现和我们的两个答案之间,有三个不同的结果。顺便说一句,在应用的情况下,您还可以使用
filter(\x->elem x[1,2,3])[5,3,2]
ff p xs ys=foldr(++)(`filter`ys).p[]xs
。我们答案之间的差异根源在于问题本身不一致。您的第一个实现没有保留被过滤列表的顺序,
ys
,这对于过滤函数来说是非常令人惊讶的行为(这是一个相关的问题,正如OP所说的“我想要过滤列表”)。至于您的第二个实现,正如您所指出的,它与问题中的用法示例不匹配。注:我建议提到第一个实现中的
(x,)
语法需要TupleSections扩展。@第一个实现中的
多过滤器((==)(2*)[9,3,14,1][5,3,2,6]
会导致
[6,2]
——元素来自
ys
,但按照
xs
@duplode中的匹配顺序排列,这也是OP代码的作用。如果我们使用这个措辞,那么它意味着第二个impl-n捕获的额外的
。@WillNess但是
被(=)[1,2,3][5,3,2]
交叉的是
[2,3]
,与OP给出的用法示例相匹配。歧义在于问题本身。@WillNess当然;我的意思是问题的一部分,即这个问题是如何形成的!所以在下面,
relate p=(join.)。liftM2(\x y->[y | p x y])
。我们不应该保留
snd
,而不是
fst
y
s,而不是
x
s)?不,
intersectBy
保留第一个列表中的元素,而不是第二个。@WillNess在
=
的情况下这有什么关系吗?我想我误解了什么。如果我们真的想保留第二个列表中的元素,我们只需翻转参数即可。这应该适用于
(不,
intersectBy
保留第一个列表中的元素,而不是第二个列表中的元素。