简体   繁体   English

Haskell - 过滤元组列表

[英]Haskell - Filtering a list of tuples

Consider this list of tuples:考虑这个元组列表:

[(57,48),(58,49),(59,50),(65,56),(65,47),(65,57),(65,49), (41, 11)]

I want to remove a tuple (a, b) if its second element b is equal to the first element of another tuple and all the tuples with the same a that come after it.我想删除一个元组(a, b)如果它的第二个元素b等于另一个元组的第一个元素以及它之后的所有具有相同a的元组。 For example:例如:

The second element of (65,57) is 57 and the first tuple in the list (57,48) has 57 as its first element, so (65,57) should be removed and all tuples that come after it that start with 65, namely (65,49) . (65,57)的第二个元素是 57 并且列表(57,48)的第一个元组有 57 作为它的第一个元素,所以(65,57)应该被删除,并且之后的所有元组以 65 开头,即(65,49) The tuples that come before it, (65,56) and (65,47) , should stay in the list.在它之前的元组(65,56)(65,47)应该留在列表中。

Does anyone have an idea how to do this?有谁知道如何做到这一点?

For efficiency (single pass), you should create two sets, one for elements you've seen as the first elements of tuples, the other for elements you've seen both as first and second elements (ie. delete if matches first element).为了提高效率(单次传递),您应该创建两个集合,一个用于您已将其视为元组的第一个元素的元素,另一个用于您已将其视为第一个和第二个元素的元素(即如果匹配第一个元素则删除) .

Something like,就像是,

{-# LANGUAGE PackageImports #-}

import "lens" Control.Lens (contains, (.~), (^.), (&))
import "yjtools" Data.Function.Tools (applyUnless, applyWhen)
import qualified "containers" Data.IntSet as Set

filterTuples :: Foldable t => t (Int, Int) -> [(Int, Int)]
filterTuples = flip (foldr go $ const []) (Set.empty, Set.empty)
  where
    go p@(x,y) go' (fsts, deletes) =
      let seenFst = fsts ^. contains y
          shouldDelete = seenFst || deletes ^. contains x
          fsts' = fsts & contains x .~ True
          deletes' = deletes & applyWhen seenFst (contains y .~ True)
      in  applyUnless shouldDelete (p:) $ go' (fsts', deletes')

EDITs: for correctness, clarity, spine-laziness编辑:为了正确性,清晰度,脊柱懒惰

You could start by creating a distinct set of all the first elements, eg:您可以从创建所有第一个元素的不同集合开始,例如:

Prelude Data.List> firsts = nub $ fst <$>
                              [(57,48),(58,49),(59,50),(65,56),(65,47),
                               (65,57),(65,49), (41, 11)]
Prelude Data.List> firsts
[57,58,59,65,41]

You could use break or span as Robin Zigmond suggests.您可以按照 Robin Zigmond 的建议使用breakspan You'll need a predicate for that.你需要一个谓词。 You could use elem , like this:您可以使用elem ,如下所示:

Prelude Data.List> elem 48 firsts
False
Prelude Data.List> elem 49 firsts
False
...
Prelude Data.List> elem 57 firsts
True

If you're concerned that elem is too inefficient, you could experiment with creating a Set and use the member function instead.如果您担心elem效率太低,您可以尝试创建一个Set并改用member函数。

I'm an absolute beginner in haskell, so there probably is a much more elegant/efficient solution for this.我是haskell的绝对初学者,因此可能有一个更优雅/更有效的解决方案。 But anyways I wanted to share the solution I came up with:但无论如何我想分享我想出的解决方案:

filterTuples :: [(Int, Int)] -> [(Int,Int)]
filterTuples [] = []
filterTuples (x:xs) = x:filterTuples(concat ((fst temp) : [filter (\z -> fst z /= del) (snd temp)]))
    where del = fst (head (snd temp))
          temp = break (\y -> (snd y == fst x)) xs 

(Glad for feedback on how to improve this) (很高兴就如何改进这一点提供反馈)

Perhaps try using mapAccumL starting with the initial list as the accumulator.也许尝试使用mapAccumL从初始列表开始作为累加器。 Then maintain a Predicate as a parameter too which acts as a decider for what has been seen, and this will determine if you can output or not at each step in the traversal.然后维护一个Predicate作为参数,它作为已看到内容的决定器,这将确定您是否可以在遍历的每一步输出。

f consumes a list of pairs: xs ; f消耗对列表: xs it produces a new list of pairs: ys .它生成一个新的对列表: ys ys contains every pair: (a, b) in xs , except the pair whose second element b : previously occurred as first elements: a . ys包含xs中的每一对: (a, b) ,除了第二个元素b :先前作为第一个元素出现的对: a When such a pair: (a, b) is encountered, subsequent pairs that have a as their first elements are excluded from ys .当遇到这样的对: (a, b)时,将a作为其第一个元素的后续对从ys中排除。

f xs = go xs [] []
  where
    go [] ys zs        = ys
    go (x@(a,b):xs) ys zs 
      |  b `elem` as  = go xs ys (a:zs)
      |  a `elem` zs  = go xs ys zs
      | otherwise     = [x] ++ go xs ys zs
    as = (nub . fst . unzip) xs

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

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