我正在尝试创建一个通过字符串数组循环的函数,将该单词添加到一个新的元组中,该元组计算一个单词在文本块中出现的次数。在OO语言中,这很简单-为每个单词及其出现的次数创建一个KV对。我正在尝试将该代码转换为Haskell,但我认为它并不那么简单。
countWords:: [String] -> [(String, Int)]
我知道我需要创建一个元组列表,但是我不确定如何使用递归遍历传递给函数的列表。
答案 0 :(得分:6)
您在OO中似乎要说的话,一个很直接的翻译是递归地遍历列表中的每个单词,或者更新已有的条目,或者将其附加为新条目。 :
registerWord :: String -> [(String, Int)] -> [(String, Int)]
registerWord w ((w',c):ws)
| w==w' = (w,c+1) : ws
| otherwise = (w',c) : registerWord w ws
registerWord w [] = [(w,1)]
然后针对每个给定的单词执行此操作,每次更新寄存器。折叠起来很容易做到:
countWords :: [String] -> [(String, Int)]
countWords = foldr registerWord []
但是,此列表插入很尴尬,并且效率低下(在FP和OO中均如此),即 O ( n 2 )。一种更好的方法是对功能进行模块化思考:您实际上希望将相等的单词分组在一起。为此,您需要先对其进行排序,这样实际上相等的单词是相邻的。然后,您需要用一个示例替换每个重复项组,并计数。好的功能管道:
countWords :: [String] -> [(String, Int)]
countWords = map (\gp@(w:_) -> (w, length gp)) . group . sort
顺便说一句,此函数中没有要求键为“单词” /字符串的内容,因此您最好将签名概括为
countWords :: Ord a => [a] -> [(a, Int)]
(另一种效率低下的方法甚至更通用,只需要Eq
。)