Haskell列表元素频率计算报错:用Map.alter与foldr实现遇问题
列表频率映射代码的问题分析
1. 函数f的类型签名完全错误
foldr的第一个参数要求类型为a -> b -> b(这里b对应Map.Map a Int),但你定义的f类型是(a -> a) -> Map.Map a Int -> Map.Map a Int,把输入参数错误地声明为函数类型,完全不符合foldr的调用要求,这是编译失败的核心原因之一。
2. Map.alter的参数使用完全不符合规范
Map.alter的标准类型是(Maybe v -> Maybe v) -> k -> Map.Map k v -> Map.Map k v,参数顺序和作用是:
- 第一个参数:处理目标键对应值的函数(接收
Maybe v,返回修改后的Maybe v) - 第二个参数:要修改的键(类型为
k,也就是这里的a) - 第三个参数:待修改的原Map
你的代码犯了两个关键错误:
- 把
Map.findWithDefault 0 x m + 1(一个Int数值)作为第二个参数传入,而正确的第二个参数应该是要更新的键x - 第一个参数传了
id,这会直接返回原来的Maybe Int值,根本不会修改计数。正确的处理函数应该实现“计数加1”的逻辑:如果键不存在(值为Nothing),就返回Just 1;如果存在(值为Just n),就返回Just (n+1)。
修正后的代码
import qualified Data.Map as Map f :: (Eq a, Ord a) => a -> Map.Map a Int -> Map.Map a Int f x m = Map.alter (\maybeCount -> Just (maybe 0 (+1) maybeCount)) x m freqs :: (Eq a, Ord a) => [a] -> Map.Map a Int freqs xs = foldr f Map.empty xs
如果一定要用Map.findWithDefault,也可以这么写(不过不如上面的方式高效,因为会额外查询一次Map):
f :: (Eq a, Ord a) => a -> Map.Map a Int -> Map.Map a Int f x m = Map.alter (\_ -> Just (Map.findWithDefault 0 x m + 1)) x m
内容的提问来源于stack exchange,提问作者coderodde
相关产品推荐
相关产品推荐

