我是编程新手,F#是我的第一个.NET语言。
我想阅读文本文件的内容,计算每个单词的出现次数,然后返回10个最常用的单词以及每个单词出现的次数。
我的问题是:在F#中使用词典吗?如果我想使用字典,我该如何编写代码? (我浏览了MSDN上的Dictionary类,但我仍然对如何将值更新为键而感到困惑。)我是否总是不得不在函数式编程中使用Map?
答案 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
。统一Reed和torbonde的答案的相关部分:
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
相同,在最后一种情况下,我们将运算符用作函数。
总的来说,倒数第二行的作用是将序列按对中第二个条目的负值(出现次数)排序。