Haskell:根据相同ID生成(Name,同ID名称列表)的编译错误排查
解决Haskell函数编译错误:根据Integer值分组Maybe String列表
我来帮你排查并解决这个Haskell的编译错误问题~先理清楚你的需求和问题点:
测试数据
datatest :: [(Maybe String, Integer)] datatest = [(Just "name1" , 111), (Just "name2" , 222), (Nothing, 333), (Just "name4",111)]
期望输出
你希望把每个元组的Maybe String,和所有拥有相同Integer值的Maybe String列表配对,得到:
[(Just "name1",[Just "name1",Just "name4"]),(Just "name2",[Just "name2"]),(Nothing,[]),(Just "name4", [Just "name1",Just "name4"])]
你的原函数代码
你编写的test123函数逻辑方向是对的,但存在类型错误:
import qualified Data.List as DL -- 推测你应该导入了这个模块,否则DL.foldl'无法识别 test123 :: [(Maybe String, Integer)] -> [(Maybe String,[Maybe String])] test123 rows = fmap (\(name,id) -> DL.foldl' (\(rowAcc,nameAcc) (fname,fid) -> case (id,fid) of (val1,val2) -> if(val1==val2) then (fname,(nameAcc++[name]) ) else (fname,nameAcc) (Nothing , _) -> (fname,nameAcc) ) ("",[]) rows ) rows
编译错误信息
编译器抛出的类型不匹配错误:
error: • Couldn't match type ‘Integer’ with ‘Maybe t0’ Expected type: [(Maybe String, Maybe t0)] Actual type: [(Maybe String, Integer)] • In the third argument of ‘DL.foldl'’, namely ‘rows’ error: • Couldn't match type ‘Integer’ with ‘Maybe t0’ Expected type: [(Maybe String, Maybe t0)] Actual type: [(Maybe String, Integer)] • In the second argument of ‘fmap’, namely ‘rows’
错误原因拆解
- 完全错误的case分支:你在
case (id,fid)里写了(Nothing, _)分支,但id和fid都是Integer类型,Nothing是Maybe类型的构造器,两者完全不兼容。这直接导致编译器试图把Integer强制转换成Maybe t0,触发类型匹配失败。 - 初始值类型不匹配:
foldl'的初始值("",[])中,第一个元素是String类型,但你的元组第一个元素是Maybe String,进一步加剧了类型推导的混乱。 - 逻辑冗余:你在
foldl'里维护了rowAcc变量,但全程没有用到它,完全是多余的。
修正后的两种实现方案
方案一:用Map分组(高效简洁,推荐)
先把所有元组按Integer值分组,构建ID到名称列表的映射,再遍历原列表匹配对应分组。时间复杂度O(n),逻辑清晰:
import qualified Data.Map as Map import Data.Map (Map) test123 :: [(Maybe String, Integer)] -> [(Maybe String, [Maybe String])] test123 rows = -- 第一步:构建ID到对应名称列表的映射 let idToNames = Map.fromListWith (++) [(id, [name]) | (name, id) <- rows] -- 第二步:遍历原列表,每个名称对应到映射中的列表;Nothing对应空列表 in map (\(name, id) -> (name, Map.findWithDefault [] id idToNames)) rows
方案二:修正原foldl'实现(保留你的思路)
如果想保留你用foldl'遍历的思路,修正类型和逻辑错误后的版本:
import qualified Data.List as DL test123 :: [(Maybe String, Integer)] -> [(Maybe String, [Maybe String])] test123 rows = fmap (\(targetName, targetId) -> -- 只需要收集匹配targetId的名称,不需要维护多余的rowAcc let matchedNames = DL.foldl' (\acc (currentName, currentId) -> if currentId == targetId then acc ++ [currentName] else acc ) [] rows in (targetName, matchedNames) ) rows
验证结果
用你的datatest测试两种方案,都会得到完全符合期望的输出:
> test123 datatest [(Just "name1",[Just "name1",Just "name4"]),(Just "name2",[Just "name2"]),(Nothing,[]),(Just "name4",[Just "name1",Just "name4"])]
内容的提问来源于stack exchange,提问作者Samuel D'costa
相关产品推荐
相关产品推荐

