【问题标题】:Looping through List and Adding to Tuple循环遍历列表并添加到元组
【发布时间】:2018-12-18 09:23:14
【问题描述】:

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

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

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

【问题讨论】:

  • 似乎是map 的一个简单用例,结合了一个计算单词在文本中出现次数的函数。但请注意,由于 Haskell 是“引用透明的”,因此您要检查的文本也需要作为参数传递给函数。

标签: list haskell tuples


【解决方案1】:

您似乎在说您在 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(n2)。更好的方法是功能模块化思考:您实际上希望将相同的单词组合在一起。为此,您需要首先对它们进行排序,因此相同的单词实际上是相邻的。然后,您需要用单个示例替换每组重复项,并计算计数。不错的功能管道:

countWords :: [String] -> [(String, Int)]
countWords = map (\gp@(w:_) -> (w, length gp)) . group . sort

顺便说一下,这个函数中没有什么要求键是“单词”/字符串,所以你不妨将签名概括为

countWords :: Ord a => [a] -> [(a, Int)]

(另一种低效的方法更通用,只需要Eq。)

【讨论】:

  • 哇。谢谢你的解释!
猜你喜欢
  • 2021-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 2022-09-30
  • 1970-01-01
相关资源
最近更新 更多