假设我有一组看起来像这样的关系:
relations :: [(A, B)]
instance Monoid A
instance Monoid B
我想将这组关系转换为A
s和B
s的一组新关系。
现在,这里有一些棘手的事情:
A
应该有B
s mappend
ed。B
应该有A
s mappend
ed。A
和B
s不同(或者不要,取决于是否可以某种方式非迭代地完成)。编辑:排序限制使问题变得微不足道,所以我删除了它。
可以假设Ord
,Hashable
或其他您需要的其他内容。出于所有意图和目的,可以说A
行为完全完全,如HashSet
和B
行为完全,如{{1} (或其他一些具有合理大小检查的类型)。
这意味着可以假设Vector
和let s = size (mappend a b); s >= size a; s >= size b
等。
此转换将如何发生的示例(假设a, b :: B; mappend a b /= mappend b a <=> a, b not mempty; a > b => (mappend a c) > b
为<a, b>
)
Set.fromList [a, b]
如何以尽可能有效的方式(时间,空间)完成?
答案 0 :(得分:2)
我认为一般情况不能比使用O(n ^ 2)合并的简单方法更好,因此总算法可以是O(n ^ 3)。在不限制列表中元素的顺序和mappend
的结果的情况下,您必须匹配每对元素以查看它们是否应该合并,并重复直到完成。
merge :: Eq e => (a -> a -> a) -> (a -> e) -> [a] -> (Bool,[a])
merge combine eqval [] = (False, [])
merge combine eqval (x:xs) = (not (null a) || t, y : zs)
where
e = eqval x
(a,b) = partition ((e ==) . eqval) xs
y = mconcat (x:a)
(t,zs) = merge combine eqval b
mergeRelations :: [(A,B)] -> [(A,B)]
mergeRelations = go False
where
mergeFsts = merge (\(a1,b1) (a2,b2) -> (a1, b1 `mappend` b2)) fst
mergeSnds = merge (\(a1,b1) (a2,b2) -> (a1 `mappend` a2, b1)) snd
go started xs
| started && not f = xs
| s = go True n
| otherwise = m
where
(f,m) = mergeFsts xs
(s,n) = mergeSnds m