Haskell实现单词计数时sort/group未生效问题排查
问题根因
你代码里计数全为1的核心问题是对列表Monad的执行逻辑理解错误,导致排序、分组操作从来没有作用在全量单词集合上,而是逐词单独执行统计:
- 你写的do代码块是运行在列表上下文里的,第一行
ys <- words xs会把输入字符串按空白切分成独立单词列表,每次取出一个单独的单词绑定到ys,执行后续所有逻辑 - 对每个单独的
ys跑正则匹配时,因为输入本身就是单个单词,匹配得到的zs永远是长度为1的单元素列表 - 后续你对这个只有1个元素的列表做转小写、排序、分组,得到的分组自然也只有1个元素,最终返回的计数永远是1
- 所有单次执行的结果拼接起来,就成了每个单词都计1的错误输出,看起来就像
sort和group没有生效——实际上这两个函数是正常工作的,只是处理的对象从来不是全句所有单词。
另外你的正则表达式还有个隐性问题:Haskell字符串里的反斜杠需要转义,你写的"\d"、"\b"不会被正则引擎识别为数字、单词边界元字符,需要写成"\\d"、"\\b"才能正常匹配。
修复方案
不要逐词切分后再跑正则,直接对原始输入字符串做全局正则匹配,拿到全量合法单词列表后,再统一做转小写、排序、分组统计即可,核心参考代码:
module WordCount (wordCount) where import qualified Data.Char as C import qualified Data.List as L import Text.Regex.TDFA as R wordCount :: String -> [(String, Int)] wordCount xs = let allWords = R.getAllTextMatches (xs =~ "\\d+|\\b[a-zA-Z']+\\b") :: [String] lowered = map (map C.toLower) allWords in map (\g -> (head g, length g)) $ L.group $ L.sort lowered
注:上述正则还需要额外调整才能正确过滤单词前后附带的撇号(比如示例里的'PASSWORD 123'会匹配到带前导撇号的'PASSWORD'),你可以根据题目规则再优化正则规则,这部分不影响本次计数全1问题的解决。
内容的提问来源于stack exchange,提问作者Abhijit Sarkar
相关产品推荐
相关产品推荐

