1、做业地址git
Github项目地址:https://github.com/gentlemanzq/WordCount.git(用的是同伴的)
github
做业连接:https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/2882算法
结对同伴博客地址:https://www.cnblogs.com/gentlemanzq/编程
个人博客地址:https://www.cnblogs.com/Ysml/框架
2、结对过程描述函数
2.1 整体状况分析性能
首先,根据题目要求,将代码分解为几个模块,确立好每一个模块的具体函数内容。其次,根据我的的编程能力,将不一样模块分配给单元测试
相应的人 进行完成。分配任务后,两人一块儿完成,其中一人完成本身相应模块的编程,另一个便审查代码,指出错误之处或者不一样学习
的思路与看法,最后两人相互讨论,选取最优的编程方法。测试
2.2 存在的问题
因为两人的编程水平存在差别,有时存在没法理解同伴编写代码的具体意思,又由于思路的不一样,存在代码具体位置的争论。
2.3 解决方法
对存在的问题,单独拉出来,由编程的人对不理解的人进行讲解,其次对相应调用函数的不认,经过百度进行了解认识。
附结对照片:
3、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 30 | 45 |
· Estimate | · 估计这个任务须要多少时间 | 60 | 60 |
Development | 开发 | 45 | 60 |
· Analysis | · 需求分析 (包括学习新技术) | 10 | 15 |
· Design Spec | · 生成设计文档 | 5 | 8 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 5 |
· Design | · 具体设计 | 20 | 30 |
Coding | · 具体编码 | 90 | 120 |
· Code Review | · 代码复审 | 45 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 80 |
Reporting | 报告 | 100 | 120 |
· Test Report | · 测试报告 | 45 | 50 |
· Size Measurement | · 计算工做量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 过后总结, 并提出过程改进计划 | 15 | 10 |
合计 | 560 | 693 |
4、解题思路
拿到题目后,先仔细阅读题目,咱们根据题目要求,构建了一个大概的框架,将题目要求分解成不一样的模块。而后根据模块的内容
找出学过相对应的代码内容。随后思考代码的可行性,是否存在漏洞。遇到不会编写的代码,上网查找资料,图书馆借阅书籍查找相关问题。
5、设计实现过程
经过审读题目要求,能够将整个程序分为以下部分:
1.统计文件字符数函数
2.统计有效行数函数
3.统计文件中各单词的出现次数函数,并只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。
4.将程序输出结果写入txt文件函数 (几个函数相互独立)
(当对代码进行封装时,就能够将以上每一个函数封装成类,须要用时,能够直接调用。)
统计文件中各单词的出现次数函数,并只输出频率最高的10个。频率相同的单词,优先输出字典序靠前的单词。为关键函数,流程图以下:
(算法关键、独特之处:提取单词的时候,一并统计了频率。而且在输出排序时,调用Orderby算法,能够直接按频率和字典序输出结果)
PS:增长客服需求新功能时,只需将一些变量变成由客户输入便可。
6、代码规范与互审
代码规范:
二、不变的值,尽可能写个常量类。
三、尽可能使用if{}else,不要一直if去判断。
四、减小循环调用方法;减小IO流的消耗资源。
5. 当一行代码太长时,将其截断成两行写。
6. 经常使用缩进和换行,使代码层次清晰,明了。
7. 注释的量不该该少于代码量的三分之一。ps(变量统一使用例如/// <param name="s">文件读入路径</param>的注释方式)
8. 定义变量名字和方法名字的时候尽可能使用英文缩写,或者拼音缩写,便于识别。
9. 对泛型进行循环时,都采用foreach而不使用for。
11. 对于功能函数写入一个function文件夹中,便于之后功能升级。
12. 一屏原则:一个方法体的代码幅应该在一屏比较和合理;逻辑复杂的代码能够抽离出方法体;
(这只是一部分,固然还有不少,就不一一列举了)
代码互审:
在编写统计字符个数时,最初代码以下:
public static int agefile()//打开文件并统计字符个数 { string fileName = @"D:\新建文本文档 (3).txt"; FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); byte[] buf = new byte[fs.Length]; fs.Read(buf, 0, buf.Length); fs.Close(); return buf.Length; }
通过代码复审后,发现打开文件和统计字符个数时过于繁杂,便有了如下代码:这样一来,代码精简不少,占用空间更少。
public static int agefile()//打开文件并统计字符个数 { string str = File.ReadAllText(@"C:\Users\LiuQi\Desktop\新建本文档案(3).txt"); int num = Regex.Matches(str, @".").Count; return num + lines() - 1; }
其次,在统计单词出现频率和按要求输出时时,最开始傻傻的认为按需求一步步编写代码就能解决。可是实际操做起来却
发现存在不少问题,许多简单状况下可以操做,在复杂状况下,存在不少不少的限制,不能轻易解决。
例如:在按字典表输出时,按原思路就是按顺序判断而后输出,作到一半都发现太麻烦了,就去百度了一下有没有简单算法,
便发现C#自带字典数排序,只需调用便可。
7、模块接口部分的性能改进
一、改进计算模块性能上所花费的时间:45分钟
2.、改进思路:将统计行数,字符数的函数封装成一个类,到时候能够根据用户需求只需改变参数便可。
三、耗时最多的函数:(运用字典数统计字符总数和其出现的频率)
public static Dictionary<string, int> Countword() { string str = File.ReadAllText(@path.s); Dictionary<string, int> frequencies = new Dictionary<string, int>(); string[] words = Regex.Split(str, @"\W+"); int k = 0; string[] newwords = ynword.ynword1(words,ref k); string[] newwords1 = new string[k]; for (int i = 0; i < k; i++) { newwords1[i] = newwords[i]; } foreach (string word in newwords1) { if (frequencies.ContainsKey(word)) { frequencies[word]++; } else { frequencies[word] = 1; } } return frequencies; }
8、单元测试与部分异常处理
在没有对函数进行封装时,咱们对各自对对方写的代码进行了单元测试
一、首先针对逻辑上第一个调用的计算行数的功能模块linescount进行测试(函数以下):
public class linescountTests { [TestMethod()] public void linesTest() { path.s = @"D:\se.txt"; int x = 0;//第一次测试时输入5,第二次输入0 Assert.AreEqual(x, linescount.lines()); // Assert.Fail(); } }
测试结果:第一次在记事本输入两行测试成功,但在输入0行时,测试失败。此处出现问题,当没有输入文本时,行数没有进行判断,因此出现错误。
2.测试asccount类(统计有多少个字符)封装的类以下:
public class asccountTests { [TestMethod()] public void asccountsTest() { path.s = @"D:\se.txt"; int num = 8; Assert.AreEqual(num, asccount.asccounts()); //Assert.Fail(); } }
测试结果:当文本不输入字符时,num=0,出现错误。思考后,发现是没有判断为0的状况,出现错误。
3.其他类通过测试并未发生什么问题。
4.功能改进后的主函数:
static void Main(string[] args) { int temp = 0; int max = 0; int len = 0; //若是命令行有参数执行 if (args.Count() != 0) { for (int i = 0; i < args.Length; i++) { if (args[i] == "-i") path.s = args[++i];//-i 命令行 else if (args[i] == "-n") max = Convert.ToInt32(args[++i]);//-n 命令行 else if (args[i] == "-o") path.outputpath = args[++i];//-o 命令行 else if (args[i] == "-m") { len = Convert.ToInt32(args[++i]); } } if (path.s == null || path.outputpath == null)//路径为空则不存在 { Console.WriteLine("路径不正确,文件不存在"); } } //命令行无参数,执行 else { Console.WriteLine("不输入参数,请手动输入读入文件路径"); string s= Console.ReadLine(); path.s = s; max = 10; Console.WriteLine("请手动输入输出路径"); string s1 = Console.ReadLine(); path.outputpath = s1; } Dictionary<string, int> frequencies = function.wordcount.Countword();//调用wordcount中方法统计单词 Dictionary<string, int> dic1Asc = frequencies.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);//按照字典序进行排序 int sum = function.wordcount.sum1(dic1Asc);//计算出单词总数量 Console.WriteLine("字符数:"+asccount. asccounts());//计算出字符数量 Console.WriteLine("单词总数:" + sum); Console.WriteLine("行数:"+linescount. lines());//计算出行数 //先按照出现次数排序,若是次数相同按照字典序排序 Dictionary<string, int> dic1Asc1 = frequencies.OrderByDescending(o => o.Value).ThenBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value); foreach (KeyValuePair<string, int> entry in dic1Asc1) { if (temp == max) break; string word = entry.Key; int frequency = entry.Value; temp++; Console.WriteLine("{0}:{1}", word, frequency); } Console.ReadKey(); }
9、心路历程与收获
首先此次的做业因为已经有了上次Github的操做经验,再一次使用时,以为不在陌生,基础操做都可以完成。在说说代码,初一看感受并非很难,可是在
实际操做过程当中,倒是发现存在各类各样的小问题。(结对编程时,两人对换行符所占字符数发生了奇异,改来改去,发现最开始的代码是对的,而从新写的
代码也是对的,白白的纠结了不少时间。)有些小问题是编写代码时发生的,有些倒是知识点漏区产生的。随后就在这些小问题中,浪费了打把的时间,可是
我的看来,效率仍是远远大于单人操做。编写代码时,同伴会在一旁发表本身的看法,也会帮忙指出忽略的错误之处,大大提升了效率,所以我以为1+1>2。