我不明白你想在你的代码中做什么,所以我必须自己建立起来。我是这样处理它的:我们将把我们的起始列表放在x 中以简化测试:
> x = [('a', "Hello"),('b', "Goodbye"), ('a', "World"), ('b', "World")]
然后导入groupBy函数:
Prelude> import Data.List
Prelude Data.List> :t groupBy
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
所以我们可以从元组的第一个元素开始分组:
> groupBy (\x y -> fst x == fst y) x
[[('a',"Hello")],[('b',"Goodbye")],[('a',"World")],[('b',"World")]]
啊,但这不起作用,因为匹配的元素不相邻,所以我们需要一个sort:
> groupBy (\x y -> fst x == fst y) $ sort x
[[('a',"Hello"),('a',"World")],[('b',"Goodbye"),('b',"World")]]
还有一个很好的函数可以让我们更简洁,on:
Prelude Data.List> import Data.Function
Prelude Data.List Data.Function> :t on
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
这让我们可以这样做:
> groupBy ((==) `on` fst) $ sort x
[[('a',"Hello"),('a',"World")],[('b',"Goodbye"),('b',"World")]]
我们想要的列表只是这些元组的后半部分:
> map (snd) $ map unzip $ groupBy ((==) `on` fst) $ sort x
[["Hello","World"],["Goodbye","World"]]
字母是前半部分——它们都是分组的,所以我们可以取其中的head:
> map (head . fst) $ map unzip $ groupBy ((==) `on` fst) $ sort x
"ab"
(将结果读作['a', 'b']——字符串只是一个字符列表。)现在我们已经很接近了,似乎应该有一种方法来组合(,)函数(元组构造) 使用我们的两个 head . fst 和 snd 函数。但我还没有想到。
相反,受@karakfa's answer 的启发,我使用了一个助手:
> mapFst f (a, b) = (f a, b)
> :t mapFst
mapFst :: (t2 -> t1) -> (t2, t) -> (t1, t)
到达:
> map (mapFst head) $ map unzip $ groupBy ((==) `on` fst) $ sort x
[('a',["Hello","World"]),('b',["Goodbye","World"])]
将. 替换为$ 会产生一个实际函数,而无需显式引用我们的x 测试值:
> (map (mapFst head) . map unzip . groupBy ((==) `on` fst) . sort) x
[('a',["Hello","World"]),('b',["Goodbye","World"])]
最后,组合在一起的两个map可以折叠起来:
> (map (mapFst head . unzip) . groupBy ((==) `on` fst) . sort) x
[('a',["Hello","World"]),('b',["Goodbye","World"])]
它的类型和我们预期的一样:
> :t (map (mapFst head . unzip) . groupBy ((==) `on` fst) . sort)
(map (mapFst head . unzip) . groupBy ((==) `on` fst) . sort)
:: (Ord b, Ord t1) => [(t1, b)] -> [(t1, [b])]