函数式编程实践记(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】

0 0
原创粉丝点击