所以我一直在为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"])]
答案 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
版本,可让您更详细地指定所需内容。
在您的情况下,您希望将“相似性”定义为“具有相同的第一个元素”。在Haskell中,“在一对中具有相同的第一个元素”意味着
(==) `on` fst
换句话说,对的第一个元素是相等的。
为了进行分组,我们将此要求提供给groupBy
,如下所示:
groupBy ((==) `on` fst) xs
在你的例子中,这将使我们回到两组:
[[("a","b"),("a","c")]
,[("b","e")]]
现在剩下的就是把这些名单变成对。其背后的基本原则是,如果我们让
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