问题描述
实现一个统计单词词频、支持Top N排序显示,并根据出现频率打印直方图的命令行工具
- 读取文件内容:读取指定文件的内容。
- 统计单词词频:将文件内容分割成单词,并统计每个单词出现的次数。
- 排序并获取Top N:根据单词出现的频率进行排序,并获取前N个结果。
- 打印直方图:根据每个单词的词频打印直方图。
这次我们要用rust实现一个wordcount统计词频的工具,学习rust的文件操作、HashMap和各种容器的转换。
Rust是当前编程语言的当红炸子鸡,它的内存安全和并发性能是其他语言无法比拟的,但是rust的门槛极高,学习曲线陡峭
所以我们需要通过实际的项目来学习rust。
测试代码
#[test]
fn test_count_words() {
let content = "Hello, world! Hello, world!";
let word_count = count_words(content);
assert_eq!(word_count.get("Hello"), Some(&2));
assert_eq!(word_count.get("world"), Some(&2));
}
#[test]
fn test_count_words_with_punctuation() {
let content = "Hello, world! Hello, world!";
let word_count = count_words(content);
assert_eq!(word_count.get("Hello"), Some(&2));
assert_eq!(word_count.get("world"), Some(&2));
}
#[test]
fn test_count_words_with_newline() {
let content = "Hello, world!\t\r\nHello, world!";
let word_count = count_words(content);
assert_eq!(word_count.len(), 2);
assert_eq!(word_count.get("Hello"), Some(&2));
assert_eq!(word_count.get("world"), Some(&2));
}
解决思路
这个程序不涉及到复杂的算法,思路和题目描述一样,主要是学习rust的文件操作、HashMap和各种容器的转换。
- 可以通过std::fs 读取文件内容,然后通过split_whitespace来分割单词
- 使用HashMap来统计单词的词频, HashMap是rust的标准库,可以直接使用
- 将HashMap转换成Vec,然后排序获取Top N
- 打印直方图,因为直方图是一个比例图表,所以需要先计算出最大值,然后根据比例来打印
代码实现
完整代码请参考:
代码解析
我们讲一下代码的实现:
第一部分: 命令行操作和文件读取
- 3-13 行: 通过env::args()获取命令行参数
- 9-12 行: top_n这个参数是一个Option类型,如果没有传入参数,就初始化一个默认值,如果参数输入不是int,也没关系,我们通过unwrap_or来得到一个默认值而不是panic
- 15行: 读出文件到一个String中
第二部分: 单词统计
- 27行: 初始化了一个HashMap,用来存储单词和词频, 因为rust会根据上下文推导类型,虽然HashMap是一个范型容器,但是初始化的时候,并不需要我们声明类型,这样会方便很多
- 29-35行: 通过split_whitespace来分割单词,然后通过entry来插入或者更新HashMap中的值, entry是HashMap的一个方法,如果key存在,就返回一个Entry,如果key不存在,就插入一个key,然后返回一个Entry, 这个写法是兼顾性能的,也就是说,如果key存在,就不会重复查找key,直接更新value
第三部分: 排序和Top N
- 21-22行: 将HashMap转换成Vec,然后通过sort_by来排序
因为我们要根据词频来排序,所以我们需要一个比较函数,这个比较函数是一个闭包,我们通过|a, b|来定义一个闭包,然后通过cmp来比较两个值
第四部分: 打印直方图
- 42行: 取得第一个的词频,这个词频就是最大值
- 44-52行: 打印直方图,通过take(top_n)来获取前N个单词,然后通过iter()来遍历
- 46-50行: 根据题目要求要做一个直方图的比例换算,如果最大的都没超过10个,那么就不需要修改比例,否则就需要修改比例,这样显示就不会出现一个单词上万次,然后打印出上万个*的情况
总结
这个题目主要是学习rust的文件操作、HashMap和各种容器的转换
Rust的写法特别严谨,和C++比较像,特别是容器和范型方面都是和C++类似的
虽然我们的题目要求比较简单,但是我们写代码的时候要考虑各种异常情况,比如文件不存在,文件读取失败,参数输入错误等等
最后我们在显示的时候,如果做一个简单的比例换算,就可以让显示效果变得很优雅,那么这种细节就对你工作特别有帮助
rust是后端的新宠,学习曲线陡峭,但是只要你坚持下去,你会发现rust的内存安全和并发性能是其他语言无法比拟的
所有的后端面试常见的问题,我们每天都会在我们的编程群里面讨论和Code review, 欢迎大家加入我们的编程群,一起学习和进步。
欢迎大家关注 入职啦 (公众号: ruzhila) ,获取更多有趣的编程挑战题和技术干货!