寒假作业2/2
| 这个作业属于哪个课程 | 2021春软件工程实践|W班(福州大学) |
|---|---|
| 这个作业要求在哪里 | 寒假作业2/2 |
| 这个作业的目标 | 阅读构建之法并提问,完成词频统计个人作业 |
| 其他参考文献 | CSDN |
目录
Part1:阅读《构建之法》并提问
提问
1.在第一章“软件工程概论”开头就提到“有人抱怨学习数据结构与算法无用”.大部分大学生学习的目的,无非就是“考试要考、面试要考、增加竞赛经历”,很少能真正地去理解其内部性质、原理。
自己在开发的过程中,也有过这样的疑惑,自己是用的大部分类都可以通过库进行调用,自己也很少会去考虑内部实现,只要懂得怎么使用就可以了;它对不做算法工程师的人而言,是否处于相对不重要的地位呢?我在网上搜索资料得到的结论:如果要做深入的软件开发,数据结构是很重要的,它有利于了解API的层次结构,可以写出高效健壮的程序,从长远来看是很有好处的。但是我有一个疑问,他们都没说清楚数据结构到底应该掌握到哪种程度,所以想请教一下老师。
2.在第3章中,提到“分析麻痹”,但书中并没有提到如何解决“分析麻痹”。
我刚开始学习写代码的时候,也有这种感觉,总是想着在开始敲代码之前,先把可能会出现的问题想清楚,想好解决措施,再动手。但这样,过于追求完美,一旦程序没有按照自己的想法展开,就容易产生消极悲观的心理。我现在写程序也有一些“畏难情绪”,觉得需要学习新技术,学了又忘,导致没有积极性,那这部分问题该如何解决呢?
3.在“结对编程”中提到“什么样的人适合结对编程”,那是不是意味着结对编程就适用于那些人,比如保守内向的人就不适合结对编程,他们可能并不习惯被人盯着工作。在结对编程的过程中,两个人的思维方式都不相同,那有分歧的部分又该如何解决呢?如果是相互解释,那会不会浪费大量的时间?结对编程的同伴又该如何选取?我是觉得选择擅长方面不同的两人,这样就能各尽其长,互帮互助。但是现实中,可能别人都已经组好队,两个能力,领域差不多的人组到了一起,那该怎么办?
我认为,对于保守内向的人,更应该进行结对编程,这样更能提高团队的凝聚力;我查资料:结对编程需要磨合期,两个人意见方面有分歧是很正常的现象。那这个磨合期又是多久呢?过了这个磨合期,还是无法经常产生分歧,是不是就说明这两人不适合在一起结对编程呢?我还有一个疑问,结对编程开始角色互换,那会不会打乱原来两人的思路呢?这样会不会导致开发效率更低了?
4.在“设计和开发”章节中的“用户体验和质量”的讨论中,举了“GE公司”的例子,让我有所思考。用户体验和质量到底哪个更重要呢?
我赞同文中总裁的看法,在不要求太多精度的检查,应该将用户体验放在第一位。毕竟如果用户体验不好的话,用户也不想再一次尝试。但是在实际开发中,用户体验和产品质量又该如何权衡呢?这个部分是由程序员来决定吗?还是由其他人员决定?
5.在“IT行业的创新”一章中,提到“创新者甚至恨创新”让我觉得很迷惑,往下看才知,创新会损害某些人的利益,最终也损害了自身的利益。以前,中国基本是模仿国外的技术,缺少创新意识;现在,创新产品不断地在中国涌现。我记得,有的面试官在面试上会问“当一个新产品上市的时候,你能将他模仿出来吗”,我觉得这不应该是招聘的要求。那么在面试的时候,会将“创新纳入考虑范围吗”,“创新”本身就是一个很抽象的概念,只通过笔试面试能准确衡量一个人的创新水平吗?
我认为任何公司都不能停下创新的步伐,否则这家公司始终都是在原地踏步。创新应该纳入考虑范围,但我觉得仅仅通过笔试和面试得到的相关信息毕竟是有限的,那么入职之后公司是否可以以“缺乏创新”为由,辞职员工呢?如果当“创新”与“利益”发生冲突的时候,公司又该如何取舍呢?
附加题
404错误,想必大家都很熟悉了。是指用户浏览网页时,服务器因某种原因无法正常提供信息或无法回应所返回的页面,即「找不到该页面」,又被称为「互联网的最后一个页面」。
关于404说法的由来众说纷纭,其中有一种说法称,404源于「404房间」。
相传互联网的第一架服务器,架设在欧洲核研究组织的404号房。如果要打开网页,就得向404房的Berners-Lee提交申请,如果他没在房间内,就会出现「404 not found」。
Part2:WordCount编程
GitHub
PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 10 | 20 |
| • Estimate | • 估计这个任务需要多少时间 | 10 | 20 |
| Development | 开发 | 420 | 525 |
| • Analysis | • 需求分析 (包括学习新技术) | 20 | 30 |
| • Design Spec | • 生成设计文档 | 10 | 10 |
| • Design Review | • 设计复审 | 10 | 10 |
| • Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 15 |
| • Design | • 具体设计 | 20 | 30 |
| • Coding | • 具体编码 | 300 | 360 |
| • Code Review | • 代码复审 | 20 | 20 |
| • Test | • 测试(自我测试,修改代码,提交修改) | 30 | 50 |
| Reporting | 报告 | 60 | 70 |
| • Test Repor | • 测试报告 | 30 | 40 |
| • Size Measurement | • 计算工作量 | 10 | 10 |
| • Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
| 合计 | 490 | 615 |
解题思路描述
需要运用java中的文件流来进行文件内容的读入和写出。
根据需求,需要实现以下几个功能:1.统计文件的字符数;2.统计文件的单词总数;3. 统计文件的有效行数; 4.统计文件中各单词的出现次数,最终只输出频率最高的10个。
具体实现:
Java中的缓冲流的性能要比非缓冲流的性能高;故使用BufferedReader和BufferedWriter分别进行文件的读写操作。
功能1:
因为测试内容全是Ascii码,无需考虑汉字,故只需直接获取数据长度即可。
功能2:
使用正则表达式进行单词判断、匹配。
功能3:
使用正则表达式进行行数统计.判断空比判断非空更简单,一开始在想非空的条件,就卡了比较久
功能4:
在实现功能1的过程中,对符合的“单词”使用map<String,Integer>来进行数量的统计(单词不区分大小写,所以统计之前需要对大写字母进行统一的小写转换);最后对map中的数据进行排序,选取前10个高频单词。排序需要用到List,将Map中的数据转移到List,重写List的compare(),
代码规范
设计与实现过程
总共有两个类:
Lib类:功能全都封装在这个类中。
WordCount类:一个主函数,用于执行命令行程序。
使用缓冲流读取文件内容
设置一个变量储存文本内容,这样就不用多次读取内容
StringBuilder fileBuilder = new StringBuilder();
bufferedReader = new BufferedReader(new FileReader(readFileName));
int c;
while ((c=bufferedReader.read())!=-1)
{
fileBuilder.append((char)c);
}
return fileBuilder.toString();
直接通过length(),获取字符数
charNum = fileContent.length();
使用正则表达式判断单词是否合法
if (str.matches("[a-zA-Z]{4}[a-zA-Z0-9]*")){
return true;
} else{
return false;
}
正则表达式分割文本,对单词进行判断,合法则单词数+1并且加入到map中
String[] contents = fileContent.split("[^a-zA-Z0-9]");
for (String content:contents)
{
if (isWord(content)){
wordNum++;
String word = content.toLowerCase();
Integer count = wordMap.get(word);
if (count==null){
count = 1;
} else {
count++;
}
wordMap.put(word,count);
}
}
正则表达式判断是否是空行
charNum = fileContent.length();
if (line.matches("\\s*")){
return false;
}
else {
return true;
}
计算空行
String[] lines = fileContent.split("\n");
for (String line:lines)
{
if (isLine(line))
{
lineNum++;
}
}
排序单词
list = new ArrayList<Map.Entry<String,Integer>>(wordMap.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String,Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
//如果词频相同,则按字典序输出
if (o2.getValue()==o1.getValue()){
return o1.getKey().compareTo(o2.getKey());
}
return o2.getValue().compareTo(o1.getValue());
}
性能改进
- 使用缓冲流BufferedReader来进行文件的读写
- 使用StringBuilder 代替String/StringBuffer 来提高效率、节约时间
- 第一次读入文件是在“字符数统计”,可以设置一个变量来存文本内容,这样只需要读一次文件,节省了时间成本。
70W字符运行速度

单元测试
测试单词合法性

测试行合法性

测试字符数

测试单词数

测试行数

测试结果

测试单词排序

排序结果:正确

测试单词数超过10

测试结果:正确

覆盖率

异常处理
在Lib类遇到IO异常时会抛出给主函数,由主函数catch后输出文件不存在!
当命令行运行时没有两个参数会输出there are not 2 parameters needed!
心路历程
- 学会正则表达式的基本使用,比起运用if判断要简洁高效许多
- 复习了文件的读写操作
- 制定了代码规范,以后都需要按照这个规范来执行,以前写代码的时候没有重视这部分,使用自己熟悉的规范,不仅自己再看时简洁,对于别人也是一样
- 第一次使用单元测试,以前测试都是通过输出语句来确认。现在意识到,单元测试很重要,能够更快地发现bug。
- 学会了使用Github,来对代码进行签入。
- 在编程之前,使用PSP表格,对该项任务进行预估;也说明了在编程前,早期的准备与思考同样很重要,这样写出来的程序更有条理性,更易于维护

浙公网安备 33010602011771号