Haskell列表合并问题:按配对首元素合并同键列表
解决Haskell列表分组拼接的问题
嘿,刚入门Haskell遇到这种分组拼接的问题很正常,我来帮你理清楚思路,以及你之前尝试不对的原因~
首先,你的需求是把首元素(也就是那些(0,1)、(1,2)这样的键)相同的元组里的列表拼接起来,核心步骤其实是两个:按首元素分组 + 拼接每个组里的所有列表。我们先看看你之前的尝试为什么没成功:
map concat list无效:因为你的列表里的元素是(a, [b])类型的元组,而concat需要的是[[b]]这种“列表的列表”类型,直接给元组用concat类型完全不匹配,自然没法工作。- 你写的
map (\t -> (filter (==).first) list ) list报错:首先语法就有问题(应该用fst而不是first,而且函数组合的写法不对),其次逻辑上也没抓住“分组”的核心——你这个写法试图对每个元素过滤整个列表,但既没正确匹配键,也没处理后续的拼接,自然会出错。
接下来给你两种实用的解决方法,选你顺手的用就行:
方法一:用sortOn + groupBy(基础列表操作)
这种方法用Haskell标准库Data.List里的函数,步骤清晰,适合理解分组逻辑:
首先需要导入必要的模块:
import Data.List (sortOn, groupBy) import Data.Function (on)
然后实现函数:
mergeLists :: Eq a => [(a, [b])] -> [[b]] mergeLists = map (concat . map snd) -- 最后一步:把每个组里的列表拼接起来 . groupBy ((==) `on` fst) -- 第二步:按首元素分组(注意要先排序让同键元素相邻) . sortOn fst -- 第一步:先按首元素排序,确保同键元素挨在一起
测试一下你的输入:
mergeLists [((0,1),[1,2,3]), ((1,2),[7,8,9]), ((0,1),[4,5,6])] -- 输出:[[1,2,3,4,5,6],[7,8,9]]
方法二:用Data.Map(更简洁的分组方式)
如果你不想手动排序,可以用标准库的Data.Map来直接按键收集列表,这种写法更简洁:
先导入模块:
import qualified Data.Map as Map
实现函数:
mergeLists' :: Ord a => [(a, [b])] -> [[b]] mergeLists' = Map.elems -- 提取Map里所有的值(就是拼接好的列表) . Map.fromListWith (++) -- 把同键的列表用(++)拼接起来
同样测试你的输入,结果和上面完全一致。这里Map.fromListWith (++)会自动遍历你的列表,遇到相同的键就把对应的列表拼接,最后用Map.elems取出所有结果即可。
两种方法的小区别:
- 方法一和方法二的结果都会按键的排序顺序输出(因为
sortOn和Map都是有序的); - 如果你的输入已经是按键排序好的,方法一可以去掉
sortOn fst这一步。
内容的提问来源于stack exchange,提问作者Miraj Tzunami
相关产品推荐
相关产品推荐

