Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List 列表-组合对_List_Haskell - Fatal编程技术网

List 列表-组合对

List 列表-组合对,list,haskell,List,Haskell,我想做一个函数,它接受一个列表 [('A',3), ('B',2), ('C',2), ('A',5), ('C',3), ('C',2)] 然后,当字母相同时,将数字相加。因此,上述输入将产生 [('A',8), ('B',2), ('C',7)]. 有人能告诉我如何处理这个问题吗?我想自己尽可能多地尝试 您可以使用Data.Map的fromListWith来构建一个包含求和值的映射。它的类型是: fromListWith :: Ord k => (a -> a ->

我想做一个函数,它接受一个列表

[('A',3), ('B',2), ('C',2), ('A',5), ('C',3), ('C',2)]
然后,当字母相同时,将数字相加。因此,上述输入将产生

[('A',8), ('B',2), ('C',7)]. 

有人能告诉我如何处理这个问题吗?我想自己尽可能多地尝试

您可以使用
Data.Map
fromListWith
来构建一个包含求和值的映射。它的类型是:

fromListWith :: Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
它将成对的列表(如您所述)作为第一个参数,如果它看到重复的,它将使用某个函数(第一个参数)将原始值与新值组合。从那里,您可以将此映射转换回成对列表(也是Data.map中的一个函数)


你可以用纯列表来实现这一点,但它可能不会那么有效,因为你会经常构建一个新的列表(或其中的一部分)。

好吧,从将问题分解成更小的部分开始

首先,忽略额外的条件。最后的目标是什么?把一些数字加在一起,你可能已经知道怎么做了

但是你不想把所有的数字都加起来,你怎么知道要加哪一个呢?看看字母是否匹配。所以你需要以某种方式比较它们

因此,一旦你知道如何添加数字,并决定是否添加任何两个数字,你需要一种方法来处理整个列表。如果将它们全部混合在一起,您将一事无成,因此您需要根据要添加的数字将它们分开。您可以通过创建子列表一次完成这一切,也可以一次筛选一个,或者使用各种其他方法,其中一些方法的效率可能高于或低于其他方法

不管怎么说,这是最基本的想法。因此,回到前面的步骤,从一个列表开始,根据比较字母将项目组分开,然后将每个结果组中的所有数字相加


我显然跳过了几个步骤——比如当字母和数字组合成元组时,如何比较字母和添加数字——因为你说过你更愿意自己解决问题。:]

有几种方法可以解决这个问题,Adam已经为您提供了一种基于地图的有效方法。然而,我认为仅使用列表和递归的解决方案也会很有启发性,尤其是在学习Haskell时。既然你已经得到了答案,我希望我能在这里写一个解决方案,而不会破坏任何东西

我处理这个问题的方法是思考如何将输入列表简化为输出列表。我们从

[('A',3), ('B',2), ('C',2), ('A',5), ('C',3), ('C',2)]
目标是以一个结果列表结束,其中每个元组以一个唯一的字符开始。构建这样一个结果列表可以增量完成:从一个空列表开始,然后在列表中插入元组,确保不重复字符。类型应该是

insertInResult :: (Char, Integer) -> [(Char, Integer)] -> [(Char, Integer)]
它获取该对,如
('A',3)
,并将其插入到唯一对的现有列表中。结果是一个新的唯一对列表。可以这样做:

insertInResult (c, n) [] = [(c, n)]
insertInResult (c, n) ((c', n'):results)
  | c == c'   = (c, n + n') : results
  | otherwise = (c', n') : (insertInResult (c, n) results)
说明:将元组插入空结果列表很容易,只需插入它即可。如果结果列表不是空的,那么我们通过模式匹配获得第一个结果
(c',n')
。我们检查字符是否与守卫匹配,如果匹配,则添加数字。否则,我们只需复制结果元组并将
(c,n)
元组插入其余结果中

我们现在可以做了

*Main> insertInResult ('A',3) []
[('A',3)]
*Main> insertInResult ('B',2) [('A',3)]
[('A',3),('B',2)]
下一步是在输入列表上重复使用
insertInResult
,以便我们建立一个结果列表。我调用了这个函数
sumPairs'
,因为我调用了顶级函数
sumPairs

sumPairs' :: [(Char, Integer)] -> [(Char, Integer)] -> [(Char, Integer)]
sumPairs' [] results = results
sumPairs' (p:pairs) results = sumPairs' pairs (insertInResult p results)
它是一个简单的函数,只需对第一个参数进行迭代,并将每个参数对插入到结果列表中。最后一步是使用空结果列表调用此helper函数:

sumPairs :: [(Char, Integer)] -> [(Char, Integer)]
sumPairs pairs = sumPairs' pairs []
它起作用了!:-)

此解决方案的复杂性不如基于
Data.Map
的解决方案。对于具有n对的列表,我们从
sumPairs'
调用
insertInResult
n次。对
insertInResult
的每次调用最多可迭代n次,直到找到匹配的结果元组,或到达结果的末尾。这使得时间复杂度为O(n²)。基于
Data.Map
的解决方案将具有O(n logn)时间复杂性,因为它使用logn time来插入和更新n个元素中的每一个

请注意,如果您对输入列表进行排序,然后扫描一次以添加具有相同字符的相邻元组,则会得到相同的复杂性:

sumPairs pairs = sumSorted (sort pairs) []

sumSorted [] result = result
sumSorted (p:pairs) [] = sumSorted pairs [p]
sumSorted ((c,n) : pairs) ((c',n') : results)
  | c == c'   = sumSorted pairs ((c,n + n') : results)
  | otherwise = sumSorted pairs ((c,n) : (c',n') : results)

除了出色的基于
Map
的解决方案之外,我认为纯粹基于列表的解决方案也很有启发性。与
映射一样,有趣的一点是使用相同的第一个元素将这些元素分组在一起。有一个内置函数
group
(及其通用版本
groupBy
),可合并相邻元素:

groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
sumSnds :: Num b => [(a,b)] -> (a,b)
sumSnds abs = (a, sum bs) where
    (a:_, bs) = unzip abs
第一个参数告诉我们何时合并两个元素。例如:

> groupBy (\x y -> odd x == odd y) [1,1,3,4,6,5,7]
[[1,1,3],[4,6],[5,7]]
不幸的是,正如我们在这里看到的,它只合并相邻的元素,因此我们需要找到一种方法,首先将原始列表中的元素聚在一起,以便那些具有相等键的元素彼此相邻。还有另一个内置函数可以执行类似的操作:
sort
。最后一个技巧是将所有第一个元素相等的块的第二个元素相加。让我们编写一个函数,假设它传递了一个包含所有相等第一个元素的非空块:

groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
sumSnds :: Num b => [(a,b)] -> (a,b)
sumSnds abs = (a, sum bs) where
    (a:_, bs) = unzip abs
现在我们可以将所有这些片段串在一起:

solution :: (Ord a, Ord b, Num b) => [(a,b)] -> [(a,b)]
solution = map sumSnds . groupBy (\x y -> fst x == fst y) . sort

比我想象的容易多了!好的。。。我希望在我的答案的早期版本中,我没有失去一些学习的机会。