小队成员:
1120161945 雷云霖
1120161949 刘镓煜
一、开发时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 5 | 6 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 120 | 120 |
· Design Spec | · 生成设计文档 | 120 | 180 |
· Design Review | · 设计复审 (和同事审核设计文档) | 10 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 20 | 30 |
· Coding | · 具体编码 | 120 | 360 |
· Code Review | · 代码复审 | 30 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 120 |
Reporting | 报告 | ||
· Test Report | · 测试报告 | 20 | 20 |
· Size Measurement | · 计算工作量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 555 | 936 |
二、思路分析与设计编码
重点在于两个方面,第一是生成不重复的随机题目,第二是如何计算这个题目。
对于题目生成的不重复性,有了个人项目数独的实战经验,便不是什么大的问题。当然在这个方面小组的成员还是有一定分歧的,小雷同学决定采用重复概率为百万分之一的随机数方法,小刘同学决定采用排列组合排出必然不相同的一千种。最后取了一个二者折中的方法,如果真发生了重复的情况,一来是对题目的复习,二来也可以告知用户他今日运气特别的好中了亿万分之一的概率,充分体现了我们软件的人性化设计——不仅仅限于做题。关键代码展示如下(C++):
string takeTest() { srand((unsigned)time(NULL)); int num1, num2; string signal; string ans = ""; signal = getSignal(6); num1 = random(0, 100); num2 = random(1, 100); if (signal == "‘") { if (num1<num2) { swap(num1, num2); } } if (signal == "**"||signal=="^") { num2 /= 35; } if (signal == "/") { if (num2 == 0) { swap(num1, num2); } } ans = ans + to_string(num1) + signal + to_string(num2); signal = getSignal(4); ans = ans + signal; signal = getSignal(6); num1 = random(0, 100); num2 = random(1, 100); if (signal == "‘") { if (num1<num2) { swap(num1, num2); } } if (signal == "**" || signal == "^") { num2 /= 35; } if (signal == "/") { if (num2 == 0) { swap(num1, num2); } } ans = ans + to_string(num1) + signal + to_string(num2); return ans; }
如何计算题目,对于我们来说也不是难事,用堆栈的方法实现中缀表达式转后缀表达式即可。能力不强的小刘同学此时发问了:那**这表示乘方的符号有两个字符串怎么办?小雷同学回答他:对算式做预处理,把**改为^。
对于分数的计算,则采取把所有操作数先通分,在分子上进行四则运算,最后约分化简的的操作。
关键代码展示如下(C++):
string MidToBack(string s) //转后缀表达式,利用栈 { string ret; stack<int> stk; int pos; while ((pos = s.find("**"))!=s.npos) //将**转换成^ { s.replace(pos, 2, "^"); } for (string::size_type i = 0; i< s.size(); i++) { if (isdigit(s[i])) { ret += s[i]; if ((i < s.size() - 1 && !isdigit(s[i + 1])) || i == (s.size() - 1)) ret += ‘ ‘;//这里是用空格将数跟符号隔开。目的是在后缀计算中提取数的时候,有些数可能不止一位,如果没有空格符,一个多位的数会被误认为是多个个位数。 } else { switch (s[i]) { case ‘(‘: stk.push(s[i]); break; case ‘)‘: while (!stk.empty() && stk.top() != ‘(‘) { ret += stk.top(); stk.pop(); } stk.pop(); break; case ‘+‘: case ‘-‘: while (!stk.empty() && stk.top() != ‘(‘) { ret += stk.top(); stk.pop(); } stk.push(s[i]); break; case ‘*‘: case ‘/‘: while (!stk.empty() && (stk.top() == ‘*‘ || stk.top() == ‘/‘ || stk.top() == ‘^‘)) { ret += stk.top(); stk.pop(); } stk.push(s[i]); break; case ‘^‘: stk.push(s[i]); break; } } } while (!stk.empty()) { ret += stk.top(); stk.pop(); } return ret; }
三、测试与优化
测试共花费2小时时间。
测试用例的设计与运行结果
1)参数的测试用例
I. -a abcd 不合法格式的参数,成功输出了错误信息
II. -a 0或-a -1 越界参数,成功输出了错误信息
III. -c 100 命令参数错误,成功输出了错误信息
2)性能优化的测试用例
-a 1000
题目生成的速度十分迅速,性能分析图不像个人项目一样波涛汹涌,反而平静如水,其中耗时最大的便是等待用户输入答案的程序段,可优化的空间不大。
经过2个人2小时的排查改进,还是发现了问题,采用随机数时产生了分母为0的错误情况,于是加了一个分母不为0的限制,解决了问题。
四、扩展需求
能力有限的小刘只能照着小雷同学的C++模板,尝试了用从未学过的JAVA语言重新编写四则运算程序。正好软件工程基础结课后开设了JAVA课程,也算对新课的一次提前了解吧。经过自学和课程的学习,小刘同学不但完成了用JAVA重新编写程序的目标,还自己尝试用JAVA基本实现了做GUI图形界面的需求。
实现算法同C++实现。
估计完成所需时间:24小时(平摊到3个星期之中),实际完成时间:24+小时
程序思路图如下:
效果如下:
五、心得体会
结对项目和个人项目体验完全不一样,个人项目只要一个人默默写就好了,而结对项目更多的是需要交流。我个人感觉受益是很大的,因为结对编程能够让一部分功能的实现趋近于最优的方式,两个人一起讨论问题怎么解决,如何做更好。举个例子来说吧,我对项目的细节经常犯错误,比如求用户正确率这样的小需求经常忘记实现了,这个时候partner都很细心的帮我指出这些,这是一个人默默写代码所无法体验到的。当然这也应该是结对项目的目的之一吧,培养大家之间的信任。
在这次合作中的确是1+1>2的体验,收获良多。
原文地址:https://www.cnblogs.com/Lylist/p/9114274.html