如何在Haskell中将[(Time, Person)]转换为含列表的[(Time, [Person])]元组列表
问题排查与修正
错误根源
- 返回类型错误:你定义的函数返回值是
[(Time, Person)],但预期输出的第二个字段是Person列表,对应返回类型应为[(Time, [Person])]。类型不匹配导致两个Person封装的字符串被直接拼接,出现Name1Name2的错误结果。 - 模式匹配不全:你仅覆盖了空列表、列表长度≥2的场景,缺少列表仅含1个元素的处理分支,因此触发“缺少穷举模式”的报错。
:操作符使用错误::是元素拼接列表的操作符,签名为a -> [a] -> [a],你代码中y : b的右侧b是单个Person类型,不是列表,类型校验不通过导致代码无法运行。
修正代码
推荐使用span函数实现同时间分组,逻辑更简洁且无匹配遗漏:
groupTogether :: [(Time, Person)] -> [(Time, [Person])] groupTogether [] = [] groupTogether ((currTime, currPerson) : rest) = -- 拆分出剩余列表中时间和当前时间相同、不同的两部分 let (sameTimeItems, diffTimeItems) = span (\(t, _) -> t == currTime) rest -- 把当前Person和所有同时间的Person拼成列表 groupPersons = currPerson : map snd sameTimeItems in (currTime, groupPersons) : groupTogether diffTimeItems
如果要保留你原来的双元素匹配写法,补全分支和类型即可:
groupTogether :: [(Time, Person)] -> [(Time, [Person])] groupTogether [] = [] -- 补全单个元素分支,把Person包装为列表 groupTogether [(x, y)] = [(x, [y])] groupTogether ((x, y) : (a, b) : ks) | x == a = groupTogether ((x, y) : ks) >>= \(t, ps) -> if t == x then [(t, b:ps)] else [(x,[y]),(a,[b])] ++ ks | otherwise = (x, [y]) : groupTogether ((a, b) : ks)
内容的提问来源于stack exchange,提问作者guib
相关产品推荐
相关产品推荐

