从元组列表中删除反转的重复项

时间:2017-05-19 08:15:15

标签: list haskell functional-programming

所以基本上我有一个元组列表[(a,b)],我必须从中做一些过滤。一个工作是删除反转的重复项,以便如果列表中存在(a,b)和(b,a),我只采用它们的一个实例。但列表理解并不是很有帮助。如何以有效的方式解决这个问题?

由于

4 个答案:

答案 0 :(得分:5)

这样做的有效方法( O(n log(n)))可能是使用Set跟踪已经添加的元组(及其反转):

import qualified Data.Set as Set                                                                                                                                                                        

removeDups' :: Ord a => [(a, a)] -> Set.Set (a, a) -> [(a, a)]
removeDups' [] _ = []
removeDups' ((a, b):tl) s | (a, b) `Set.member` s = removeDups' tl s
removeDups' ((a, b):tl) s | (b, a) `Set.member` s = removeDups' tl s
removeDups' ((a, b):tl) s = ((a, b):rest) where
    s' = Set.insert (a, b) s
    rest = removeDups' tl s'

removeDups :: Ord a => [(a, a)] -> [(a, a)]
removeDups l = removeDups' l (Set.fromList [])

函数removeDups使用列表和空集调用辅助函数removeDups'。对于每一对,如果它或它的逆在集合中,则通过;否则,添加它和它的反转,并处理尾部。 \

复杂度为 O(n log(n)),因为在每个步骤中,集合的大小在 n 中最多是线性的。

示例

...

main = do
    putStrLn $ show $ removeDups [(1, 2), (1, 3), (2, 1)]

$ ghc ord.hs && ./ord
[1 of 1] Compiling Main             ( ord.hs, ord.o )
Linking ord ...
[(1,2),(1,3)]

答案 1 :(得分:3)

您可以使用自己的功能过滤它们:

checkEqTuple :: (a, b) -> (a, b) -> Bool
checkEqTuple (x, y) (x', y') | (x==y' && y == x') = True
                             | (x==x' && y == y') = True
                             | otherwise          = False

然后使用nubBy

Prelude Data.List> nubBy checkEqTuple [(1,2), (2,1)]
[(1,2)]

答案 2 :(得分:2)

我觉得我有点重复自己,但那没关系。这些代码都没有经过测试甚至编译过,因此可能存在错误。假设我们可以对效率施加Ord约束。我将从一组有限的实现开始。

import qualified Data.Set as S
import qualified Data.Map.Strict as M

newtype PairSet a b =
  PS (M.Map a (S.Set b))

empty :: PairSet a b
empty = PS M.empty

insert :: (Ord a, Ord b)
       => (a, b) -> PairSet a b -> PairSet a b
insert (a, b) (PS m) = PS $ M.insertWith S.union a (S.singleton b) m

member :: (Ord a, Ord b)
       => (a, b) -> PairSet a b -> Bool
member (a, b) (PS m) =
  case M.lookup a m of
    Nothing -> False
    Just s -> S.member b s

现在我们只需要跟踪我们见过哪些对。

order :: Ord a => (a, a) -> (a, a)
order p@(a, b)
  | a <= b = p
  | otherwise = (b, a)

nubSwaps :: Ord a => [(a,a)] -> [(a,a)]
nubSwaps xs = foldr go (`seq` []) xs empty where
  go p r s
    | member op s = r s
    | otherwise = p : r (insert op s)
    where op = order p

答案 3 :(得分:0)

如果a和b是有序且可比的,你可以这样做:

[(a,b) | (a,b) <- yourList, a<=b]