简体   繁体   English

Haskell - 通过比较过滤列表

[英]Haskell - Filtering Lists by Comparison

I need to filter a list with a mask in Haskell.我需要在 Haskell 中过滤带有掩码的列表。

It applies the function to an element of the mask list and the corresponding element in the data list, and if the function returns true, the corresponding element in the data list is included in the returned list.它将函数应用于掩码列表的元素和数据列表中的相应元素,如果函数返回true,则返回列表中包含数据列表中的相应元素。

For example, suppose I want to filter out data items that are greater than the mask items:例如,假设我想过滤掉大于掩码项的数据项:

filtermask (\m d -> d > m) [1, 5, 7] [5, 6, 7]→[5, 6]

Any possible solution to this problem would be fantastic.这个问题的任何可能的解决方案都会很棒。 So far, I've only done到目前为止,我只做了

 filtermask f m d = f m d

That returns TRUE返回 TRUE

EDIT: Solution, thanks to help from Tikhon:编辑:解决方案,感谢来自 Tikhon 的帮助:

filtermask f [] [] = []
filtermask f (fm:rm) (fd:rd)
  |f fm fd = fd:filtermask f rm rd
  |otherwise = filtermask f rm rd

Fundamentally, what you want to do is first pair up each element of the mask with an element of the list.从根本上说,您首先要做的是将掩码的每个元素与列表的一个元素配对。 We can do this with zip , which gives us a list of pairs.我们可以用zip来做到这一点,它给了我们一个对的列表。

Now that we have a list of pairs, we want to filter this using the comparison function ( > , or whatever).现在我们有一个对的列表,我们想使用比较函数( >或其他)过滤它。 The > function has a type Ord o => o -> o -> Bool ; >函数的类型为Ord o => o -> o -> Bool we need to turn this into a function which accepts a tuple.我们需要把它变成一个接受元组的函数。 Happily, we can do this with uncurry ;令人高兴的是,我们可以用uncurry来做到这uncurry uncurry (>) gives us a function of type Ord o => (o, o) -> Bool . uncurry (>)给了我们一个Ord o => (o, o) -> Bool类型的函数。

Since we already have a list of pairs, we can just use this function to filter it.由于我们已经有了一个对列表,我们可以使用这个函数来过滤它。 Now we have a list of only the pairs we want to keep;现在我们只有我们想要保留的对的列表; we need to get a list of just the element.我们需要得到一个只包含元素的列表。 We can do this by mapping a projection function like fst over the list.我们可以通过在列表上映射一个像fst这样的投影函数来做到这一点。

Putting it all together, we get:把它们放在一起,我们得到:

filterMask :: (o -> o -> Bool) -> [o] -> [o] -> [o]
filterMask fn mask list = map fst (filter (uncurry fn) (zip list mask))

There's just one more subtle trick: what happens if the mask is shorter than our input list?还有一个更微妙的技巧:如果掩码比我们的输入列表短会怎样? With this function, the rest of the input list will be thrown out.使用此函数,输入列表的其余部分将被丢弃。 If this is not what you want, you will need to repeat the mask.如果这不是您想要的,您将需要重复面膜。 We can do this with a neat function called cycle which repeats a list forever.我们可以使用一个称为cycle的简洁函数来做到这一点,该函数永远重复一个列表。 So that version would be:所以那个版本是:

filterMask fn mask list = map fst (filter (uncurry fn) (zip list (cycle mask)))

As you problem is design, two list of the same size, and a function working and their element, using the zip function is a good starting point.由于您的问题是设计,两个相同大小的列表,以及一个工作的函数及其元素,使用 zip 函数是一个很好的起点。 It's work like this,工作是这样的

# zip [1,2,3] [4,5,6] 
[(1,4),(2,5),(3,6)]

Then, for each tuple, you have to check if the predicate is fill or not.然后,对于每个元组,您必须检查谓词是否填充。
It's like to apply a zip with a function, there is a function for that.这就像应用带有功能的 zip 一样,有一个功能可以做到这一点。

# zipWith (\x y -> x + y) [1,2,3] [4,5,6] 
[5,7,9]

But, the applied function is a more than the one provided.但是,应用的功能不仅仅是提供的功能。
And, what are we supposed to do if the predicate is not satisfied ?而且,如果谓词不满足,我们应该怎么做?
These two point are manage by a conditional statement and Maybe type.这两点由条件语句和Maybe类型管理。
Finally we obtain a list containing, two kind of value Nothing and Just x, and we'd like to keep the last one.最后我们得到一个包含两种值Nothing 和Just x 的列表,我们想保留最后一个。 catMaybe do the job. catMaybe可以胜任。

# filtermask f m d = catMaybes $ zipWith (\x y -> if f x y then Just y else Nothing) m d

The problem should be broken down into two parts:问题应该分为两部分:

  1. Producing the boolean mask from comparing the two lists通过比较两个列表生成布尔掩码
  2. Filtering one of the lists based on the boolean mask根据布尔掩码过滤列表之一

For 1, you can use zipWith .对于 1,您可以使用zipWith For 2, you can use maskFilter :对于 2,您可以使用maskFilter

maskFilter :: [Bool] -> [a] -> [a]
maskFilter mask xs = [x | (m,x) <- zip mask xs, m]

For your example, you can use对于您的示例,您可以使用

let
    xs = [1,5,7]
    ys = [5,6,7]
    mask = zipWith (>) ys xs
in
    maskFilter mask ys

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM