F#:生成字数统计摘要

时间:2015-03-20 19:22:08

标签: f# word-count

我是编程新手,F#是我的第一个.NET语言。

我想阅读文本文件的内容,计算每个单词的出现次数,然后返回10个最常用的单词以及每个单词出现的次数。

我的问题是:在F#中使用词典吗?如果我想使用字典,我该如何编写代码? (我浏览了MSDN上的Dictionary类,但我仍然对如何将值更新为键而感到困惑。)我是否总是不得不在函数式编程中使用Map?

3 个答案:

答案 0 :(得分:3)

  

我的问题是:在F#中使用词典吗?

使用字典可以很好地使用F#,虽然它确实使用了可变性,所以它并不常见。

  

如果我想使用字典,我该如何编写代码?

如果你读了这个文件,并且有一个逗号分隔值的字符串,你可以 解析使用类似于:

的东西
// Just an example of input - this would come from your file...
let strings = "one, two, one, three, four, one, two, four, five"
let words = 
    strings.Split([|','|]) 
    |> Array.map (fun s -> s.Trim())

let dict = Dictionary<_,_>()
words
|> Array.iter (fun w -> 
    match dict.TryGetValue w with
    | true, v -> dict.[w] <- v + 1
    | false, _ -> dict.[w] <- 1)

// Creates a sequence of tuples, with (word,count) in order
let topTen =
    dict
    |> Seq.sortBy (fun kvp -> -kvp.Value)
    |> Seq.truncate 10
    |> Seq.map (fun kvp -> kvp.Key, kvp.Value)

答案 1 :(得分:3)

虽然其他答案没有任何问题,但我想指出已经有一个专门的函数来获取序列中唯一键的数量:Seq.countBy。统一Reedtorbonde的答案的相关部分:

let countWordsTopTen (s : string) =
    s.Split([|','|]) 
    |> Seq.countBy (fun s -> s.Trim())
    |> Seq.sortBy (snd >> (~-))
    |> Seq.truncate 10

"one, two, one, three, four, one, two, four, five"
|> countWordsTopTen
|> printfn "%A" // seq [("one", 3); ("two", 2); ("four", 2); ("three", 1); ...]

答案 2 :(得分:2)

我想说这个任务的一个明显选择是使用Seq模块,它实际上是F#中的主要工具之一。正如里德所说,使用字典并不常见,因为它是可变的。另一方面,序列是不可变的。如何使用序列执行此操作的示例是

let strings = "one, two, one, three, four, one, two, four, five"
let words =
  strings.Split([|','|]) 
  |> Array.map (fun s -> s.Trim())

let topTen =
  words
  |> Seq.groupBy id
  |> Seq.map (fun (w, ws) -> (w, Seq.length ws))
  |> Seq.sortBy (snd >> (~-))
  |> Seq.truncate 10

我认为这些代码本身就说得很好,尽管第二行可能需要简短的解释: snd - 函数给出了一对中的第二个条目(即snd (a,b)b),>>是函数组合运算符(即(f >> g) a是相同的如g (f a))和~-是一元减运算符。请注意,运算符本质上是函数,但是当它们作为函数使用(并声明)它们时,必须将它们包装在括号中。也就是说,-3(~-) 3相同,在最后一种情况下,我们将运算符用作函数。

总的来说,倒数第二行的作用是将序列按对中第二个条目的负值(出现次数)排序。