如果condition为true,则合并多个列表

时间:2013-05-09 14:09:35

标签: haskell

我一直试图绕过这一段时间,但似乎我缺乏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]]

感谢您的帮助!

2 个答案:

答案 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]]

最明显的问题是我们得到四个副本:两个来自将这些列表与自己合并,两个来自将这些列表彼此合并“两个方向”。出现此问题的原因是ListSet不同,因此我们无法杀死唯一身份用户。当然,这是一个简单的解决方案,我们只会在任何地方使用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无法成为ApplicativeOrd的实例。< / p>

我还注意到Set中有很多丢失的行为。例如,当我们的关系是对称的时候,我们要么在列表中丢弃订单信息,要么必须同时处理x <> yy <> 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]]