我一直试图绕过这一段时间,但似乎我缺乏Haskell经验并不能让我完成它。我在Stackoverflow上找不到类似的问题(大多数都与合并所有子列表有关,没有任何条件)
所以这就是它。假设我有一个这样的列表列表:
[[1, 2, 3], [3, 5, 6], [20, 21, 22]]
如果某种条件成立,是否有合并列表的有效方法?假设我需要合并至少共享一个元素的列表。例如,结果将是:
[[1, 2, 3, 3, 5, 6], [20, 21, 22]]
另一个例子(当所有列表都可以合并时):
[[1, 2], [2, 3], [3, 4]]
结果是:
[[1, 2, 2, 3, 3, 4]]
感谢您的帮助!
答案 0 :(得分:4)
我不知道如何评价效率,但我们可以打破正在发生的事情并至少获得几种不同的功能。特定功能可能是可以优化的,但重要的是要明确说明需要什么。
让我重新解释一下这个问题:对于某些集合X,一些二元关系R和一些二元运算+,产生一个集合Q = {x + y | X中的x,X中的y,xRy}。因此,对于您的示例,我们可能将X作为一组列表,R为“xRy,当且仅当x和y中至少有一个元素时”,和+ ++
。
一个天真的实现可能只是复制set-builder符号本身
shareElement :: Eq a => [a] -> [a] -> Bool
shareElement xs ys = or [x == y | x <- xs, y <- ys]
v1 :: (a -> a -> Bool) -> (a -> a -> b) -> [a] -> [b]
v1 (?) (<>) xs = [x <> y | x <- xs, y <- xs, x ? y]
然后p = v1 shareElement (++) :: Eq a => [[a]] -> [[a]]
可能达到你想要的效果。除非它可能没有。
Prelude> p [[1], [1]]
[[1,1],[1,1],[1,1],[1,1]]
最明显的问题是我们得到四个副本:两个来自将这些列表与自己合并,两个来自将这些列表彼此合并“两个方向”。出现此问题的原因是List
与Set
不同,因此我们无法杀死唯一身份用户。当然,这是一个简单的解决方案,我们只会在任何地方使用Set
import Data.Set as Set
v2 :: (a -> a -> Bool) -> (a -> a -> b) -> Set.Set a -> Set.Set b
v2 (?) (<>) = Set.fromList . v1 (?) (<>) . Set.toList
因此,我们可以在p = v2 (shareElement
上使用
Set.toList) Set.union
Prelude Set> p $ Set.fromList $ map Set.fromList [[1,2], [2,1]]
fromList [fromList [1,2]]
似乎有用。请注意,我们必须“完成”List
,因为由于Set
约束,Monad
无法成为Applicative
或Ord
的实例。< / p>
我还注意到Set
中有很多丢失的行为。例如,当我们的关系是对称的时候,我们要么在列表中丢弃订单信息,要么必须同时处理x <> y
和y <> x
。
可以编写一些更方便的版本,如
v3 :: Monoid a => (a -> a -> Bool) -> [a] -> [a]
v3 r = v2 r mappend
如果我们假设关系是一个平等关系,那么可以构建更高效的关系,而不是O(n^2)
操作,我们可以在O(nd)
中d
执行此操作。 }是关系的分区数(陪集数)。
一般来说,这是一个非常有趣的问题。
答案 1 :(得分:2)
我碰巧在这里写了类似的东西:Finding blocks in arrays
你可以修改它(虽然我对效率不太确定):
import Data.List (delete, intersect)
example1 = [[1, 2, 3], [3, 5, 6], [20, 21, 22]]
example2 = [[1, 2], [2, 3], [3, 4]]
objects zs = map concat . solve zs $ [] where
areConnected x y = not . null . intersect x $ y
solve [] result = result
solve (x:xs) result =
let result' = solve' xs [x]
in solve (foldr delete xs result') (result':result) where
solve' xs result =
let ys = filter (\y -> any (areConnected y) result) xs
in if null ys
then result
else solve' (foldr delete xs ys) (ys ++ result)
输出:
*Main> objects example1
[[20,21,22],[3,5,6,1,2,3]]
*Main> objects example2
[[3,4,2,3,1,2]]