F#:生成字数统计摘要

F#: Generating a word count summary

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

我想读取一个文本文件的内容,统计每个词出现的次数,然后return最常见的10个词和每个词出现的次数。

我的问题是:在 F# 中是否鼓励使用字典?如果我想使用字典,我该如何编写代码? (我浏览了 MSDN 上的字典 class,但我仍然对如何将值更新为键感到困惑。)我是否总是必须在函数式编程中求助于使用 Map?

My questions are: Is using a dictionary encouraged in F#?

在 F# 中使用字典很好,尽管它确实使用了可变性,因此并不常见。

How would I write the code if I wish to use a dictionary?

如果您阅读该文件,并且有一个以逗号分隔值的字符串,您可以 使用类似于以下内容的解析:

// 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)

我想说这个任务的一个明显选择是使用 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 相同,在最后一种情况下我们将运算符用作函数。

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

虽然其他答案没有任何问题,但我想指出,已经有一个专门的函数可以获取序列中唯一键的数量:Seq.countBy。将 's and 的答案的相关部分放在一起:

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); ...]