项目 | 内容 |
---|---|
这个做业属于哪一个课程 | https://edu.cnblogs.com/campus/buaa/BUAA_SE_2019_LJ |
这个做业的要求在哪里 | https://edu.cnblogs.com/campus/buaa/BUAA_SE_2019_LJ/homework/2638 |
我在这个课程的目标是 | 熟悉结对编程流程,进行一次结对开发 |
这个做业在哪一个具体方面帮助我实现目标 | 进行结对编程实践,了解告终对编程的优缺点 |
最长单词链项目html
PSP 2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 1830 | |
· Estimate | · 估计这个任务须要多少时间 | 1830 | |
Development | 开发 | 1620 | |
· Analysis | · 需求分析 (包括学习新技术) | 200 | |
· Design Spec | · 生成设计文档 | 60 | |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | |
· Design | · 具体设计 | 200 | |
· Coding | · 具体编码 | 600 | |
· Code Review | · 代码复审 | 200 | |
· Test | · 测试(自我测试,修改代码,提交修改) | 300 | |
Reporting | 报告 | 210 | |
· Test Report | · 测试报告 | 150 | |
· Size Measurement | · 计算工做量 | 30 | |
· Postmortem & Process Improvement Plan | · 过后总结, 并提出过程改进计划 | 30 | |
合计 | 1830 |
Information Hiding(信息隐藏)原则,是David Parnas在1972年最先提出信息隐藏的观点。他在其论文中指出:代码模块应该采用定义良好的接口来封装,这些模块的内部结构应该是程序员的私有财产,外部是不可见的。是指程序的具体实如今外部是不可见的,只暴露出一些接口。
Interface Design,良好的接口设计须要遵循单一职责原则,开放-封闭原则,里氏替换原则等,在本次做业中,Core类只暴露出2个接口。
Loose Coupling,松耦合,一个模块对另外一个模块的调用较少。
在本次做业设计中,Core模块仅暴露出2个接口gen_chain_word()和gen_chain_char(),用户对于内部的具体实现是不知道的。node
计算模块包含一个Core类,其中仅有两个可供外部调用的接口:git
int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop); int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
Core类中的其余函数:程序员
void newnode(string word); void addtomap(node newword); void toforest(); void next1(vector<int> forward, int root); void next2(vector<int> forward, int root); int findmostwords(char head, char tail); int findlongest(char head, char tail); int listlength(int index);
各函数之间的调用流程图以下:
github
算法大体思路:
根据单词链的定义,一个单词的最后一个字母等于另外一个单词的第一个字母,故想到用有向图来实现。算法
花费时间:
改进计算模块性能所花费的时间:2小时。编程
改进思路:
第一个版本的程序中,不管面对什么状况,都会把有向图中的大大小小的全部链路都分出来,花费了大量的时间。在不约束首位字母和只约束首字母的状况下,只按照各个根节点来生成最长的单词链。在约束尾字母的状况下,再加入对尾字母的判断,生成更多的链。app
性能分析图以下:
ide
从性能分析图来看,消耗最大的函数是listlength(),该函数用来计算单词链的长度,具体代码以下:函数
int Core::listlength(int index) { int sum = 0; int i = 0; for (i = 0; i < forest[index].size(); i++) { sum += map[forest[index][i]].wordlen; } return sum; }
Design by Contract(契约编程),Design by Contract使用了三类断言:后继条件(post-conditions),前提条件(pre-conditions),以及不变量(invariants)。
契约编程好处在于程序员只需按照以前制定好的契约来对本身的代码负责,可是契约式编程很是的繁琐,这也就是缺点。
在咱们本次的做业中,并无严格知足契约式编程。
对Core模块进行了多个测试,如下举出2例
测试示例一代码以下:
TEST_METHOD(TestMethod5)//-h a -t t -w { Core* core = new Core(); char* result[100]; char* words[] = { "abbbb","bcccc","cdddd","deee","ct" }; char* answer[] = { "abbbb","bcccc","ct"}; int answerlen = 3; int resultlen = core->gen_chain_word(words, 5, result, 'a', 't', false); Assert::AreEqual(resultlen, answerlen); int i = 0; for (i = 0; i < answerlen; i++) { string stranswer = answer[i]; string strresult = result[i]; Assert::AreEqual(stranswer, strresult); } }
这是一个约束单词链首字母为a,末尾字母为t,单词数量最多的测试,调用Core模块中的gen_chain_word接口。这是一个测试程序可否正确跑对的测试。
测试示例二代码以下:
TEST_METHOD(TestMethod8)//-h b -t t -c { Core* core = new Core(); char* result[100]; char* words[] = { "abb","bccccccccccccccccccccc","cccccccccccccccccccccf","ctttt","cbt" }; char* answer[] = { "bccccccccccccccccccccc","ctttt" }; int answerlen = 2; int resultlen = core->gen_chain_char(words, 5, result, 'b', 't', false); Assert::AreEqual(resultlen, answerlen); int i = 0; for (i = 0; i < answerlen; i++) { string stranswer = answer[i]; string strresult = result[i]; Assert::AreEqual(stranswer, strresult); } }
这个测试一样也是约束首尾字母分别为b,t的测试,但不一样的是,要求输出字母最多的单词链,在所给的输入数字中存在一个干扰项,即“bccccccccccccccccccccc”,"cbt"。来检验程序是否可以正确执行。
在Core模块的单元测试中,总共构造了12个测试。获得的测试覆盖率以下:
此异常应对当输入的单词可以构成单词环可是并无输入-r参数的状况。
单元测试样例以下
TEST_METHOD(ErrorTest1)//包含单词环可是没有-r参数 { Core* core = new Core(); char* result[100]; char* words[] = { "apple","elephant","tea","alex","box","xob","cccccff","football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc" }; char* answer[] = { "football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc","cccccff" }; int answerlen = 3; try { int resultlen = core->gen_chain_char(words, 9, result, '\0', 'f', false); } catch (exception e) { Assert::AreEqual("单词文本隐含单词环", e.what()); } }
其中 football -- llllllllllllllc --- cccccff 能造成单词环,可是 enable_loop 传入 false,应该抛出“单词文本隐含单词环”异常。
计算模块接口经过char* words[] 来传入所有单词,若是单词字符串中出现不是字母的状况,就应该抛出此异常。
单元测试样例以下
TEST_METHOD(ErrorTest2)//输入的单词中有非法字符,elephant 的字母l被空格代替 { Core* core = new Core(); char* result[100]; char* words[] = { "apple","e ephant","tea","alex","box","xob","cccccff","football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc" }; char* answer[] = { "football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc","cccccff" }; int answerlen = 3; try { int resultlen = core->gen_chain_char(words, 9, result, '\0', 'f', false); } catch (exception e) { Assert::AreEqual("单词包含非法字符", e.what()); } }
输入的单词中的第二个单词"e ephant"的第二个字符不是字母,应该抛出”单词包含非法字符“异常。
当传入的head 和 tail 参数既不是合法字母,也不是'\0'字符时抛出异常。
单元测试样例以下
TEST_METHOD(ErrorTest3)//首尾字母约束不合法,用 -h * { Core* core = new Core(); char* result[100]; char* words[] = { "apple","elephant","tea","alex","box","xob","cccccff","football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc" }; char* answer[] = { "football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc","cccccff" }; int answerlen = 3; try { int resultlen = core->gen_chain_char(words, 9, result, '*', '\0', false); } catch (exception e) { Assert::AreEqual("首尾字母约束不合法", e.what()); } }
在调用gen_chain_char()时,head参数输入为'*',既不是字母也不是'\0',不合法,应当抛出“首尾字母约束不合法”异常。
当传入的words[]中有空字符串的时候,抛出此异常。
单元测试样例以下
TEST_METHOD(ErrorTest5)//有单词为空字符串 " { Core* core = new Core(); char* result[100]; char* words[] = { "apple","","tea","alex","box","xob","cccccff","football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc" }; char* answer[] = { "football","lllllllllllllllllllllllllllllllllllllllllllllllllllllllllc","cccccff" }; int answerlen = 3; try { int resultlen = core->gen_chain_char(words, 9, result, '\0', '\0', false); } catch (exception e) { Assert::AreEqual("有单词为空字符串", e.what()); } }
传入的words[]中的第二个是 “” , 为空字符串,应当抛出“有单词为空字符串”异常。
(1)首先经过main函数从命令行中获取参数的个数和具体的参数
int main(int argc, char* argv[])
从第二个参数开始依次读取argv[]中的参数,与"-h","-t","-r","-w","-c"这五个参数进行比较,进行具体处理。
若是是"-h"或者"-t"这两个约束首尾字母的参数,则直接读取argv[]中的下一个参数,根据规则,紧跟着的下一个参数应该是字母,若是不是则报错。
若是是“-w"或者“-c”这两个参数,则直接读取argv[]中的下一个参数,根据规则,紧跟着的下一个参数应该是单词文本的路径,若是不是则报错。
其中,“-w","-c"参数不能同时存在,"-h",”-t“参数能够同时存在,同一个参数不能出现两次,所以增长了五个布尔类型的变量进行是否重复的判断。
以判断”-h“参数为例,部分代码以下
for (i = 1; i < argc; i++) { parameter = argv[i]; if (strcmp(parameter, "-h") == 0) { if (if_head) { cout << "错误:-h参数重复" << endl; exit(0); } if_head = true; i++; parameter = argv[i]; if (strlen(parameter) == 1 && isalpha(parameter[0])) { head_alpha = parameter[0]; } else { cout << "错误:-h后没有字母" << endl; exit(0); } }
在main函数读入参数以后,直接进行命令行参数的处理,判断参数都正确以后,根据"-w" 和 "-c" 参数来分别调用Core模块中的两个接口进行计算
if (if_word) { resultlen = core->gen_chain_word(words, wordslen, result, head_alpha, tail_alpha, if_roun); } else if (if_char) { resultlen = core->gen_chain_char(words, wordslen, result, head_alpha, tail_alpha, if_roun); }
PSP 2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务须要多少时间 | 1830 | 1430 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 200 | 180 |
· Design Spec | · 生成设计文档 | 60 | 20 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 10 |
· Design | · 具体设计 | 200 | 30 |
· Coding | · 具体编码 | 600 | 600 |
· Code Review | · 代码复审 | 200 | 180 |
· Test | · 测试(自我测试,修改代码,提交修改) | 300 | 220 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 150 | 200 |
· Size Measurement | · 计算工做量 | 30 | 10 |
· Postmortem & Process Improvement Plan | · 过后总结, 并提出过程改进计划 | 30 | 10 |
合计 | 1830 | 1430 |