函数式编程实践记(1)——统计单词频率
来源:互联网 发布:乾坤nb买卖源码 编辑:程序博客网 时间:2024/06/05 16:10
“舅,这次要给我讲解啥子函数式?” YY 问道
“简单点,知道你java基础不错,那就来个 java实现统计单词频率问题” 我答道
题目
给定一段英文,找出每个单词使用的频率, 按照String字典顺序排序,并打印出所有单词及其频率的排序列表。如: “My name is …, I come from …, I am … years old!”
结果为:
am 1
come 1
from 1
i 2
is 1
my 1
name 1
old 1
years 1
【木丁糖 http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。[Q群联系我:631353571】
“舅, 你不是说你会Kotlin嘛, 秀一下呗?” YY 挑衅的说。
“哟,可以啊,激将! 没事,那就秀一段给你瞧下, 看好了” 说完,只听见键盘敲击声。不一会,代码展示如下:
Kotlin实现
fun main(args: Array<String>) { val wordString = "My name is ..., I come from ..., I_m... he's ... 10 years old! my" wordString .toLowerCase() //*转小写 .replace(Regex("[^a-z0-9A-Z\\s]"), "") //*替换不是字母数字空格的字符为 "" .split(" ") //*空格分词 .filter { it.matches(Regex("\\w+")) } //*过滤单词 .sortedBy { it } //排序 .groupBy { it } //分组 .toList() // 转换为列表 .sortedByDescending { it.second.size } // 按照词频高低排序 .forEach { (key, value) -> println("$key: ${value.size}") } //打印}
结果如下: 最酷炫的是,在一个长长的链式中实现了我们所要的功能。统计词频,按照词频高低排序,词频相同的按照字母字典排序。
my: 2
10: 1
come: 1
from: 1
hes: 1
i: 1
im: 1
is: 1
name: 1
old: 1
years: 1
YY 脑袋凑到我跟前,盯着屏幕,不由的发出 “喔 **, Kotlin这么简洁,而且还是实现了 按照词频 & 字母顺序排序, 牛 x”
“那是,也不看下是谁写的, 你看到思路了没?” 转头我问YY
“大概看懂了”YY回复说
“说说看” 我再次给YY挖坑
“喏,不都是写了注释么? ” YY 指着注释跟我说
“晕~~, 思路是清晰的,也就是其中的排序稍微麻烦些,其他没有难点”我鄙视的说
“YY,那你说说,Java的思路?” 我挑衅的问到
“没问题,只不过我的慢慢来实现” YY 回复到
【木丁糖 http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。[Q群联系我:631353571】
思路
给定一段英文,首先需要识别其中的每个单词,再统计每个单词出现的频率,再对单词进行字母顺序排列,并打印其中出现的频率。这里给出一个可行的思路:
1. 函数传入(String), 将单词放到Map<String, Integer>
中, 使用正则表达式识别单词
2. 遍历所有找到单词, 将首次遇到单词 加入Map, 将重复遇到单词的出现次数加 1
3. 遍历打印 key, value
【木丁糖 http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。[Q群联系我:631353571】
Java版本
YY说出了他的思路:
给定一段英文,首先需要识别其中的每个单词,再统计每个单词出现的频率,再对单词进行字母顺序排列,并打印其中出现的频率。
1. 函数传入(String), 将单词放到Map<String, Integer>
中, 使用正则表达式识别单词
2. 遍历所有找到单词, 将首次遇到单词 加入Map, 将重复遇到单词的出现次数加 1
3. 遍历打印 key, value
private static Map<String, Integer> wordFreq(String wordsString) { TreeMap<String, Integer> wordMap = new TreeMap<>(); Matcher matcher = Pattern.compile("\\w+").matcher(wordsString); while (matcher.find()) { String word = matcher.group().toLowerCase(); if (wordMap.get(word) == null) { wordMap.put(word, 1); }else { wordMap.put(word, wordMap.get(word) + 1); } } return wordMap; }
“TreeMap 有自动排序功能, Matcher实现分词。 该方法将输入的String 中单词统计并放入到TreeMap中,其中的Key,是单词本身,value,单词的次数 ” YY 解释道
打印方式如下:
String wordsTemp = "My name is ..., I come from ..., I am ... years old!";for (Entry<String, Integer> entry : wordFreq(wordsTemp).entrySet()) { out.println(entry.getKey() + " " + entry.getValue()); }
舅点评:
“YY, Java传统实现中,你这个算是比较优雅的实现了,是Java高级使用。但是,这是命令方式的实现“
”怎么说?“ YY 疑惑的问
”没有发现? 我们需要深入到细节里面, 每个小细节都需要自己实现。第一步做什么,第二部做什么,为了效率,最好都在一个循环内实现。而且,你还偷懒了 把正则表达式匹配单词 写成【”\w+”】, 这个算是细节了,就不追究了“ 我故作恐吓的数落YY
”我都说了,我是个半吊子,没法跟大神比“ YY 辩解道
”莫嘴贫,你试着用java 8 实现下?“ 我回复道
”OK, 我就知道你会说java 8,我会!“ YY 故作镇定的答复
【木丁糖 http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。[Q群联系我:631353571】
Java 8实现
”我是参考: Java 8 中的 Streams API 详解 这篇文章熟悉 Java 8 Stream 常用API的 ” YY 首先解释下
“霍, 这么厉害,还找到这篇参考文章,可以嘛” 我鼓励着YY
YY 给出了他自己的java 8 思路。
函数式思路
YY没有刻意去记Stream 的各种API,只是慢慢的接触这些API,再慢慢使用就可以了。我更在乎YY的思路。针对题目来说,YY跳出之前的思维框框。以下列文字为例, 找出其中的单词,并统计出现的频率?
String wordsTemp = “My name is …, I come from …, I’m 10 … years old! my”;
函数式,有个特点,高度抽象,怎么理解? 不要深入实现的细节,这里的细节可以让Java 8 Stream帮你实现。
对于这个题目,思路可以抽象为:
1. 分词 -> 将一句话中的每个单词分开, 单词之间是以: ” “分开
2. 过滤 -> 过滤不是单词的符号
3. 排序 -> 按照字母字典排序
4. 单词分组统计
不一会儿,YY给出了他的java 8 版本一
【木丁糖 http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。[Q群联系我:631353571】
java 8 版本一
private static void textCount(@NotNull String text) { Arrays.stream(text.toLowerCase() //小写 .split(" ")) //分词 .filter(word -> word.matches("\\w+")) //过滤 .sorted(Comparator.naturalOrder()) //字典排序// .forEach(s -> out.println(s)); //调试打印 .collect(groupingBy(word -> word)) //分组归类 .forEach((key, value) -> out.println(key + ": " + value.size())); //打印 key, value }
我指出了 YY 遇到的问题
注意 这个版本是有问题的, 不信看结果。结果少了 I’m, old! 为什么?
name: 1
i: 1
is: 1
from: 1
come: 1
my: 2
years: 1
10: 1
这是因为少考虑了替换,需要把某些字符 ’ !消灭掉,所以,在分词之前加入替换的正则表达式。
【木丁糖 http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。[Q群联系我:631353571】
版本二
private static void textCount(@NotNull String text) { Arrays.stream(text.toLowerCase() .replaceAll("[^a-z0-9A-Z\\s]", "") //*新增替换特殊字符* .split(" ")) .filter(word -> word.matches("\\w+")) .sorted(Comparator.naturalOrder())// .forEach(s -> out.println(s)); .collect(groupingBy(word -> word)) .forEach((key, value) -> out.println(key + ": " + value.size())); }
结果:
im: 1
old: 1
name: 1
i: 1
is: 1
from: 1
come: 1
my: 2
years: 1
10: 1
我还是看到YY的问题: 最后的 (key, value) (单词 单词频率) 没有排序,其实在 .sorted(Comparator.naturalOrder())
后,排序好了,但是 .collect(groupingBy(word -> word))
破坏了, groupingBy并不是字典顺序来分组,他是乱序的。而且还因为 collect
返回值不是Stream, 导致无法再次使用 【.】 去做进一步的函数调用,所以 这里没有办法使用一个函数调用链 来实现。只能把 Arrays.stream
的返回值保存到 一个Map中,然后再使用Map迭代方式去排序再打印。参考实现:
private static void textCount(@NotNull String text) { Map<String, List<String>> map = Arrays.stream(text.toLowerCase() .replaceAll("[^a-z0-9A-Z\\s]", "") .split(" ")) .filter(word -> word.matches("\\w+")) .sorted(Comparator.naturalOrder()) .collect(groupingBy(word -> word)); TreeMap<String, Integer> treeMap = new TreeMap<>(); for(Entry<String, List<String>> entry : map.entrySet()) { treeMap.put(entry.getKey(), entry.getValue().size()); } treeMap.forEach((key, value) -> out.println(key + " " + value)); }
“YY, 你完成的不错,虽然不能像Kotlin那样一个链式就搞定了,那是java 8 的问题,而不是你的问题,你已经上道了,后续多练习总结,超越我指日可待” 我故意夸夸YY
“不用给安慰,还是有差距的啊!”YY 认真的说
“看你颓废样,大神又不是说出来的,是真刀真枪干出来的” 我鼓励的说到
【木丁糖 http://blog.csdn.net/shrimpcolo 未经允许严禁转载,请尊重作者劳动成果。[Q群联系我:631353571】
- 函数式编程实践记(1)——统计单词频率
- Groovy高效编程——统计单词频率
- hadoop-python——统计单词出现的频率
- Java程序—统计英文短文单词频率
- 统计单词频率-map
- Java统计单词频率
- Java编程:统计文本文件中单词出现频率
- 统计单词频率(HashMap)
- 作业,统计单词出现频率
- Java统计单词出现频率
- 统计文本单词频率————软件工程第一次作业
- 模拟MapReduce编程的程序案例(用于统计文本中单词出现频率)
- 利用树统计单词出现的频率
- java实现文件单词频率统计
- 统计文本中单词使用频率
- 统计文件中单词出现的频率
- 统计英文文本单词出现频率
- 统计文本中英文单词的出现频率
- PTA 基础编程题目集 函数题 4-1~4-13
- 继承
- SSD参考
- 列表数据类型
- ASCII Art—使用纯文本来显示图像:综述
- 函数式编程实践记(1)——统计单词频率
- monkeyrunner.bat运行python脚本/命令行
- 二叉树的后序遍历
- 小萌新的进阶之路
- 第四次作业-判断成绩
- rem移动适配
- Arduino的多任务管理
- 经典排序算法
- monkey脚本编写