有条件地合并列表元素

时间:2015-05-29 10:08:51

标签: haskell

我还是哈斯克尔的新手,但我努力学习它。但现在我来到了一个地步,我想我必须学习一个全新的haskell篇章。

所以这是包含这些数字的Int列表:

[[5,6,14],[1,2,9],[11,12,13],[6,13,14],[5,13,14]]

在开始时,所有内部列表都包含三个数字。目标是将所有重叠列表合并为更大的列表:

[[5,6,14],[1,2,9],[11,12,13],[6,13,14],[5,13,14]]
merging elements at index 0 and 3 because of the common 6 in both lists.
[[5,6,14,13],[1,2,9],[11,12,13],[5,13,14]]
merging elements at index 0 and 2 because of the common 13 in both lists.
[[5,6,14,13,11,12],[1,2,9],[5,13,14]]
merging elements at index 0 and 2 because of the common 5,13 & 14 in both lists.
[[5,6,14,13,11,12],[1,2,9]]

这应该是该功能的结果。列表中列表的顺序与最里面列表中元素的顺序无关。

我知道如何使用任何其他命令式语言对此进行编码,但此处我被卡住了。

1 个答案:

答案 0 :(得分:1)

使用List数据结构执行此操作的一种方法如下所示。最初编写一个谓词函数,用于查明两个列表之间是否有任何共同元素:

hasCommon :: Eq a => [a] -> [a] -> Bool
hasCommon xs ys = not . null $ intersect xs ys

λ> hasCommon [1,2,3] [3]
True
λ> hasCommon [1,2,3] [4]
False

现在,您可以实现一个solve功能,它将为您提供所需的功能:

solve :: Eq a => [[a]] -> [[a]]
solve xs = case xs' of
             [] -> []
             x'@(x:[]) -> x'
             x'@(x1:x2:[]) -> x'
             x'@(x1:x2:xs) -> x1:(solve (x2:xs))
    where xs' = aux (head xs) (tail xs) []
          aux :: Eq a => [a] -> [[a]] -> [[a]] -> [[a]]
          aux acc [] temp = (acc:temp)
          aux acc (y:ys) temp = if (acc `hasCommon` y)
                                then aux (union acc y) (ys ++ temp) []
                                else aux acc ys (y:temp)

该功能的主要部分是aux。该函数接受三个参数。该函数的第一个参数是输入列表的初始头,用于将其与输入列表的其余部分进行比较。 aux函数的第二个参数是输入列表的tail。第三个参数是您保留的临时变量,用于跟踪已处理的元素。您在函数中处理两种情况:如果输入列表的尾部为空,则返回[acc] ++ temp,它将保存您的结果。这将在另一个案例中建立,现在将对此进行描述。在另一种情况下,检查输入列表的头部是否具有输入列表尾部的任何公共元素。如果它有任何共同的元素,那么看看我如何将值传递给aux函数。基本上我用ys ++ temp填充第二个输入,以便遍历已经遍历的元素。第一个参数是xsy的并集。在这种情况下,其他部分是不言自明的。

ghci演示:

λ> solve [[5,6,14],[1,2,9],[11,12,13],[6,13,14],[5,13,14]]
[[5,6,14,13,11,12],[1,2,9]]
λ> solve [[5,6,14],[18,19,20],[5,13,14],[1,2,9],[17,18,19],[20,21,22]]
[[5,6,14,13],[18,19,20,21,22,17],[1,2,9]]