Haskell编译器为何推断packHelper为[[a1]]->[[a1]]而非预期类型?
解析pack函数的编译错误与修复方案
我来帮你拆解下这个编译错误的根源,以及怎么修复你的pack函数:
核心错误原因
你代码里的语法问题直接导致了类型推断混乱,看这行递归调用:
packHelper ( (splitOff:acc) (drop (length splitOff) zs) )
Haskell里函数调用是靠空格分隔参数的,你却把两个参数splitOff:acc和drop (length splitOff) zs用括号包在了一起。这会让编译器误以为splitOff:acc是一个函数,然后把drop...的结果传给它——但splitOff:acc的类型是[[a]],根本不是函数类型!
这就连锁引发了一系列类型错误:
- 编译器被迫错误推断
packHelper是接受[[a1]]返回[[a1]]的单参数函数,和你预期的[[a]] -> [a] -> [[a]]完全不符; - 最后调用
packHelper [[]] xs时,你传了两个参数,但编译器认为它只接受一个,自然报错; - 把
splitOff:acc当函数调用,类型不匹配的错误也随之而来。
另外还有个逻辑小问题:你用[[]]作为初始累加器,最后得到的结果会是反转的,还多了一个空列表,完全不符合测试用例的预期。
修复后的代码
先修正语法错误,再调整累加器和结果顺序:
pack :: Eq a => [a] -> [[a]] pack xs = let packHelper acc [] = reverse acc -- 反转累加器得到正确顺序 packHelper acc zs = let splitOff = takeWhile (\y -> y == head zs) zs remaining = drop (length splitOff) zs in packHelper (splitOff : acc) remaining -- 正确传递两个参数 in packHelper [] xs -- 初始累加器用空列表而非[[]]
测试验证
把你的测试用例代入:
pack ['a', 'a', 'a', 'a', 'b', 'c', 'c', 'a', 'a', 'd', 'e', 'e', 'e', 'e'] -- 输出:["aaaa","b","cc","aa","d","eeee"],完美符合预期
额外优化小建议
你用head zs是安全的(因为空列表分支已经单独处理),但用模式匹配取第一个元素会让代码更清晰:
pack :: Eq a => [a] -> [[a]] pack xs = let packHelper acc [] = reverse acc packHelper acc (z:zs') = let splitOff = z : takeWhile (== z) zs' remaining = drop (length splitOff) (z:zs') in packHelper (splitOff : acc) remaining in packHelper [] xs
内容的提问来源于stack exchange,提问作者Petras Purlys
相关产品推荐
相关产品推荐

