遍历列表并添加到元组

时间:2018-12-18 09:23:14

标签: list haskell tuples

我正在尝试创建一个通过字符串数组循环的函数,将该单词添加到一个新的元组中,该元组计算一个单词在文本块中出现的次数。在OO语言中,这很简单-为每个单词及其出现的次数创建一个KV对。我正在尝试将该代码转换为Haskell,但我认为它并不那么简单。

countWords:: [String] -> [(String, Int)]

我知道我需要创建一个元组列表,但是我不确定如何使用递归遍历传递给函数的列表。

1 个答案:

答案 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。)