第三次作业

一、我们Fork仓库的Github项目地址

结对使用的Github项目地址:https://github.com/xuyixiaowoaini/WordCount.git

结对伙伴的作业地址:https://www.cnblogs.com/CHIQING123/p/10659152.html

二、PSP表格

PSP2.1

Personal Software Process Stages

预估耗时(分钟)

实际耗时(分钟)

Planning

计划

 5  5

· Estimate

· 估计这个任务需要多少时间

 5  5

Development

开发

 720  810

· Analysis

· 需求分析 (包括学习新技术)

 60  120

· Design Spec

· 生成设计文档

 10  10

· Design Review

· 设计复审 (和同事审核设计文档)

 10  10

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 10  10

· Design

· 具体设计

 30  60

· Coding

· 具体编码

 360  360

· Code Review

· 代码复审

 60  60

· Test

· 测试(自我测试,修改代码,提交修改)

 180  180

Reporting

报告

 130  190

· Test Report

· 测试报告

 60  120

· Size Measurement

· 计算工作量

 10  10

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 60  60
 

合计

 855  1005

 

三、计算模块接口的设计与实现过程。

1、设计

这个项目有五个基本功能点:计算Input文件的字符数、单词总数、有效行数、单词出现次数并排序,以及输出到Result文件。

这些功能总体由三个类来完成:

(1)WordIO(实现输入文件以及输出文件的功能)

(2)WordTrie(实现计算单词出现次数并排序的功能)

(3)WordCalculate(实现计算字符数、单词总数、有效行数的功能)

大致结构如下:

2、如何体现“Design by Contract”、“Information Hiding”、 “Interface Design”、 “Loose Coupling”等原则的

(1)Design By Contract(契约式设计):

按照某种规定对一些数据等作出约定,如果超出约定,程序将不再运行。

如何体现:一个契约设计,就是约束了某个方法调用的要求、以及返回的承诺。那么,设计在正确的输入下,能够得到正确的输出,否则程序将报错。

(2)Information Hiding(信息隐藏):

在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是不可访问的。

如何体现:类与类之间通过接口类访问。

(3)Interface Design(接口设计):

对接口的名字,功能,接口与接口间的继承关系进行设计;好的接口设计可以增强代码可读性,易用性,可更改性。

如何体现:设计接口,规范接口名字,注重接口逻辑。

 (4)Loose Coupling 松耦合

软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。

如何体现:增加接口。

四、代码复审过程。

1、代码规范

参考https://wenku.baidu.com/view/b5be911b6bd97f192279e9bd.html

2、代码互审情况、发现的问题

 我们完成这个项目时基本是在一起讨论编程的,因此最后没有发现什么大的问题,只是合并时改了不同的参数,以及对代码规范的问题进行了一些修改。

五、计算模块接口部分的性能改进。

1、性能改进

    在整体编程结束后,进行性能分析与改进是非常有必要的。虽然在小规模的数据下,功能的实现是不太会涉及到优化算法的。但是,个别特例或者是大规模的数据却需要对程序进行性能的优化改进。我查询资料找到这些方法:

(1)消除循环的低效率

(2)减少过程调用

(3)消除不必要的存储器引用

(4)循环展开

(5)提高并行性

    在性能改进方面则运用了这些方法来改进以提高性能。

2、性能分析图

六、代码说明。

在关键部分已加入注释说明。

(1)WordIO类,实现输入文件以及输出文件的功能

 1 public class WordIO
 2     {
 3         public string pathIn;
 4         public string pathOut;
 5 
 6         //按行读取输入文件并统计
 7         public WordCalculate Input(WordCalculate datanumber, WordTrie wtrie)
 8         {
 9             FileStream fs = null;
10             StreamReader sr = null;
11             String dataline = String.Empty;
12             try
13             {
14                 fs = new FileStream(this.pathIn, FileMode.Open);
15                 sr = new StreamReader(fs);
16                 while ((dataline = sr.ReadLine()) != null)
17                 {
18                     datanumber.Calculate(dataline, wtrie);  //按行统计数据
19                 }
20             }
21             catch { Console.WriteLine("文档读取失败!"); }
22             finally
23             {
24                 if (sr != null) { sr.Close(); }
25                 if (fs != null) { fs.Close(); }
26             }
27             return datanumber;
28         }
29 
30         //将统计数据输出并写到输出文件
31         public void Output(WordCalculate datanumber, WordTrie wtrie)
32         {
33             FileStream fs = null;
34             StreamWriter sw = null;
35             List<WordTrie.ListUnit> WordList = new List<WordTrie.ListUnit>();
36             try
37             {
38                 fs = new FileStream(this.pathOut, FileMode.Create);
39                 sw = new StreamWriter(fs);
40                 WordList = wtrie.Sort();
41                 sw.WriteLine("字符总数为:{0}", datanumber.charactersnumber);
42                 sw.WriteLine("单词总数为:{0}", datanumber.wordsnumber);
43                 sw.WriteLine("有效行数为:{0}", datanumber.linesnumber);
44                 sw.WriteLine("\n词频\t单词\n");
45                 Console.WriteLine("字符总数为:{0}", datanumber.charactersnumber);
46                 Console.WriteLine("单词总数为:{0}", datanumber.wordsnumber);
47                 Console.WriteLine("有效行数为:{0}", datanumber.linesnumber);
48                 Console.WriteLine("\n词频\t单词\n");
49                 for (int i = 0; (i < 10 && i < WordList.Count); i++)
50                 {
51                     sw.WriteLine("{0}\t{1}",WordList[i].WordNum, WordList[i].Word);
52                     Console.WriteLine("{0}\t{1}",WordList[i].WordNum,  WordList[i].Word);
53                 }
54             }
55             catch { Console.WriteLine("文档写入失败!"); }
56             finally
57             {
58                 if (sw != null) 
59                 { 
60                     sw.Close(); 
61                 }
62                 if (fs != null) 
63                 {
64                     fs.Close();
65                 }
66             }
67         }
68     }

 

(2)WordTrie类,利用Trie树节点实现计算单词出现次数并排序的功能(只展示关键函数)

 1 //获取单词词频
 2         public int WordCount(string word)
 3         {
 4             return GetCount(word, true);
 5         }
 6 
 7         private int GetCount(string str, bool isword)
 8         {
 9             if (string.IsNullOrEmpty(str))
10             {
11                 return -1;
12             }
13             TrieNode node = _Root;
14             for (int i = 0, len = str.Length; i < len; i++)
15             {
16                 char pos = str[i];
17                 if (!node.Sons.ContainsKey(pos)) return 0;
18                 else node = node.Sons[pos];
19             }
20             return isword ? node.WordNum : node.PrefixNum;
21         }

 

 1 //词频排序
 2         public List<ListUnit> Sort()
 3         {
 4             TrieNode node = _Root;
 5             List<ListUnit> WordList = new List<ListUnit>();
 6             WordList = WordPreOrder(node, WordList);
 7             //按词频降序排列,若词频相等按字典序排列
 8             WordList.Sort((a, b) =>
 9             {
10                 if (a.WordNum.CompareTo(b.WordNum) != 0)
11                     return -a.WordNum.CompareTo(b.WordNum);
12                 else
13                     return a.Word.CompareTo(b.Word);
14             });
15             return WordList;
16         }

 

(3)WordCalculate(实现计算字符数、单词总数、有效行数的功能)

 1 public class WordCalculate
 2     {
 3         public long charactersnumber = 0;  //统计数据:字符数
 4         public long wordsnumber = 0;  //统计数据:单词数
 5         public long linesnumber = 0;  //统计数据:行数
 6         //数据统计
 7         public void Calculate(string dataline, WordTrie wtrie)
 8         {
 9             if (string.IsNullOrEmpty(dataline)) return;
10             string word = null;
11             for (int i = 0, len = dataline.Length; i < len; i++)
12             {
13                 char unit = dataline[i];
14                 if (unit >= 65 && unit <= 90) 
15                 { 
16                     unit = (char)(unit + 32); 
17                 }  //大写字母转换成小写
18                 if ((unit >= 48 && unit <= 57) || (unit >= 97 && unit <= 122))
19                 {
20                     word = String.Concat(word, unit);
21                 }
22                 else
23                 {
24                     if (!string.IsNullOrEmpty(word))  
25                     {
26                         if ((word[0] >= 97 && word[0] <= 122) ) 
27                         { 
28                             wtrie.Insert(word); 
29                         }
30                         word = null;
31                     }
32                 }
33             }
34             if (!string.IsNullOrEmpty(word))  
35             {
36                 if ((word[0] >= 97 && word[0] <= 122) ) 
37                 { 
38                     wtrie.Insert(word); 
39                 }
40                 word = null;
41             }
42             this.linesnumber++;  //统计行数
43             this.wordsnumber = wtrie.CountSum;  //统计单词数
44             this.charactersnumber += dataline.Length;  //统计字符数
45         }
46     }

 运行结果:

七、计算模块部分单元测试展示。 

单元测试代码是根据可能出现的异常而设计出的,(部分单元测试代码见第八)。

单元测试得到的测试覆盖率截图如下:

八、计算模块部分异常处理说明。 

1、单词以英文字母开头

 1 [TestMethod()]
 2         public void MainTest01()
 3         {
 4             string test;
 5             Program.Result trueres = new Program.Result();
 6             string reason;
 7             test = "123abcd";
 8             trueres.charactersnumber = 7;
 9             trueres.wordsnumber = 0;
10             trueres.linesnumber = 1;
11             reason = "测试用例1";
12             UnitTest(test, trueres, reason);
13 
14         }

123abcd不是一个单词。若错误会计算出单词数为1。

2、单词以分隔符分割

 1 [TestMethod()]
 2         public void MainTest02()
 3         {
 4             string test;
 5             Program.Result trueres = new Program.Result();
 6             string reason;
 7             test = "abcd abcd@abcd";
 8             trueres.charactersnumber = 14;
 9             trueres.wordsnumber = 3;
10             trueres.linesnumber = 1;
11             reason = "测试用例2";
12             UnitTest(test, trueres, reason);
13         }

单词以空格和非字母数字符号作分隔符分隔。若错误会计算出单词数为1。

3、统计有效行数

 1 TestMethod()]
 2         public void MainTest03()
 3         {
 4             string test;
 5             Program.Result trueres = new Program.Result();
 6             string reason;
 7             test = "abcd\n\nabcd";
 8             trueres.charactersnumber = 8;
 9             trueres.wordsnumber = 2;
10             trueres.linesnumber = 2;
11             reason = "测试用例3";
12             UnitTest(test, trueres, reason);
13         }

有效行数指任何包含非空白字符的行。测试设计了一个空白行,若错误会计算出行数为3。

4、单词:字母后可跟数字

 1 [TestMethod()]
 2         public void MainTest04()
 3         {
 4             string test;
 5             Program.Result trueres = new Program.Result();
 6             string reason;
 7             test = "abcd123";
 8             trueres.charactersnumber = 7;
 9             trueres.wordsnumber = 1;
10             trueres.linesnumber = 1;
11             reason = "测试用例4";
12             UnitTest(test, trueres, reason);
13         }

若错误则计算出单词个数不为1。

 

九、结对的过程及结对照片。

1、结对过程:

 我和与我结对的黄欣同学,先把项目需要完成的功能划分成几个模块,再一起填写psp表格并进行分工。我们分别完成各自分配的模块,中途遇到的问题一起讨论查资料解决,最后我们将代码合并起来。

 2、结对照片:

 

十、解决项目的心路历程与收获,以及结对感受

1、由于编程不熟悉,以及在实际的开发过程中遇到种种问题的阻挠,使得完成这个项目实际花费的时间远远大于预估计划的时间,并且一些功能也没有完善的很好,我感受到以后的学习还有很长的路需要去努力。

2、结对感受:第一次结对完成一个项目,我的感受是1+1>2。因为在编程中,两个人除了独立完成自己那部分的功能任务,还能够在互审阶段会更容易发现对方自己不易察觉的问题,不仅如此,两人合作会使遇到的困难更容易被解决,学习中得到的进步也更大。而在这一次结对的实际操作中,让我对结对编程的理解更加深入了。

 

posted @ 2019-04-05 16:56  粉色激光炮  阅读(207)  评论(2编辑  收藏  举报