将元组的第二个元素组合到基于第一个元素的列表中

时间:2017-12-14 01:22:30

标签: haskell

我一直在尝试根据元组中的第一个元素来获取字符串列表。

Ex: 
[('a', "Hello"),('b', "Goodbye"), ('a', "World"), ('b', "World")] 
-> 
[('a', ["Hello","World"]),('b',["Goodbye","World"])]

我认为我已经非常接近,但我没有得到正确的输出。

map (foo . unzip) . groupBy (\st en -> fst st == fst en) . sort 
     where edge (name, futureList) = (head name, futureList)

我猜它最终与futureList有关。我认为需要附加一个功能。

3 个答案:

答案 0 :(得分:4)

我不明白你在代码中想要做什么,所以我必须自己构建它。以下是我接触它的方法:我们将我们的起始列表放在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 . fstsnd函数。但我还没想出来。

相反,受到@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])]

答案 1 :(得分:2)

你有名字混淆(foo vs edge)

基本上是相同的代码

import Data.List(sort,groupBy)
import Data.Function(on)

map (mapFst head . unzip) . groupBy ((==) `on` fst) . sort 
    where mapFst f (a, b) = (f a, b)

给出

[('a',["Hello","World"]),('b',["Goodbye","World"])]

答案 2 :(得分:0)

您也可以使用fromListWith中的Data.Map.Strict来执行此操作:

import Data.Map.Strict (fromListWith, toList)
import Data.List (sort)

groupTuples :: [(Char, String)] -> [(Char, [String])]
groupTuples tuples = [(k, sort v) | (k, v) <- grouped]
    where grouped = toList $ fromListWith (++) [(k, [v]) | (k, v) <- tuples]

其工作原理如下:

*Main> groupTuples [('a', "Hello"),('b', "Goodbye"), ('a', "World"), ('b', "World")]
[('a',["Hello","World"]),('b',["Goodbye","World"])]