在Haskell中加入列表

时间:2013-10-24 23:04:49

标签: list haskell

所以我一直在为Haskell做准备,我做得很好,直到我被困在这个练习中。基本上我想要一个接收这样一个列表的函数:

xs = [("a","b"),("a","c"),("b","e")]

返回类似这样的内容:

xs = [("a",["b","c"]), ("b",["e"])].

我想出了这段代码:

list xs = [(a,[b])|(a,b) <- xs]

但问题是这不符合我的要求。我猜它很接近,但不对。

以下是返回的内容:

xs = [("a",["b"]),("a",["c"]),("b",["e"])] 

3 个答案:

答案 0 :(得分:1)

如果您不关心最终列表中元组的顺序,最有效的方法(不重新发明轮子)将使用{{1}中的Map类型在Data.Map包中:

containers

如果你关心结果顺序,你可能不得不做一些丑陋的事情和O(n ^ 2)这样:

import Data.Map as Map

clump :: Ord a => [(a,b)] -> [(a, [b])]
clump xs = Map.toList $ Map.fromListWith (flip (++)) [(a, [b]) | (a,b) <- xs]

main = do print $ clump [("a","b"),("a","c"),("b","e")]

答案 1 :(得分:1)

您可以使用Data.Map.insertWith右侧折叠:

import Data.Map as M hiding (foldr)

main :: IO ()
main = print . M.toList
             $ foldr (\(k, v) m -> M.insertWith (++) k [v] m)
                     M.empty
                     [("a","b"),("a","c"),("b","e")]

输出:

./main
[("a",["b","c"]),("b",["e"])]

答案 2 :(得分:1)

基本原则是要将“相似”元素组合在一起。

每当您想要将元素组合在一起时,group中就会有Data.List个函数。在这种情况下,您希望自己指定相似的内容,因此您需要使用groupBy版本。 Data.List中的大多数功能都有By版本,可让您更详细地指定所需内容。

第1步

在您的情况下,您希望将“相似性”定义为“具有相同的第一个元素”。在Haskell中,“在一对中具有相同的第一个元素”意味着

(==) `on` fst

换句话说,对的第一个元素是相等的。

为了进行分组,我们将此要求提供给groupBy,如下所示:

groupBy ((==) `on` fst) xs

在你的例子中,这将使我们回到两组:

[[("a","b"),("a","c")]
,[("b","e")]]

第2步

现在剩下的就是把这些名单变成对。其背后的基本原则是,如果我们让

ys = [("a","b"),("a","c")]

作为一个例子,取第一对的第一个元素,然后将所有对的第二个元素粉碎成一个列表。采取第一对的第一个元素很容易!

fst (head ys) == "a"

采取所有第二个元素也相当容易!

map snd ys == ["b", "c"]

这两项行动共同为我们提供了我们想要的东西。

(fst (head ys), map snd ys) == ("a", ["b", "c"])

成品

因此,如果您愿意,可以将您的丛生功能编写为

clump xs = (fst (head ys), map snd ys)
  where ys = groupBy ((==) `on` fst) xs