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