20165318 结对编程项目-四则运算 阶段总结
目录
一、需求分析
-
能随机生成n道四则运算题目,n由使用者输入
-
支持整数和分数
-
支持多运算符
-
能够判断正误,错误时能提醒并输出正确答案
-
能计算出正确率
-
能多次生成题目,直到使用者选择退出
后续拓展的可能
-
题目去重
-
文件:
- 处理生成题目并输出到文件
- 完成题目后从文件读入并判题
-
多语言支持:简体中文, 繁體中文, English
二、设计思路(同时输出UML类图)
1、首先考虑生成题目。随机生成题目,用到Random
函数,生成题目应该是同时随机生成数字和四则运算符号,此处用MakeQuestions
子类,新建一个空字符串用于存放生成的题目;
2、考虑到用户的需求设计从键盘输入来控制生成的题目数量和题目复杂度(也就是四则运算的符号数);
3、生成题目之后进行运算,要求用逆波兰算法来运算题目,此处参考了逆波兰算法实现四则运算,将中缀表达式变成后缀表达式,再调用计算的类进行运算。
4、RationalNumber
实现真分数的计算。
5、题目输出后就从键盘输入结果进行答题,eqauls
比较录入的结果和经过运算的值,答对一题记正确题目数加一,最后用该值除以题目数得到正确率,实现判题功能。
- UML图
三、实现过程中的关键代码解释
中缀转后缀方法实现
参考2016-2017-2 《Java 程序设计》课堂实践项目:
设立一个栈,存放运算符,首先栈为空;
从左到右扫描中缀式,若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符;
若遇到运算符,则与栈顶比较,比栈顶级别高则进栈,否则退出栈顶元素并输出,然后输出一个空格作分隔符;
若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。
当栈变成空时,输出的结果即为后缀表达式。
public void conversion(String expr) { //中缀转后缀
String token;
StringTokenizer tokenizer = new StringTokenizer(expr);
while (tokenizer.hasMoreTokens()) {
//当tokenizer有下一个值时,进行循环,并把值赋给token
token = tokenizer.nextToken();
if (token.equals("(")) {
//如果是左括号,入栈
stack.push(token);
}else if (token.equals("+") || token.equals("-")) {
//如果是“+”或“-”,继续判断栈是否为空
if (!stack.empty()){
//如果栈非空,判断栈顶元素是什么
if (stack.peek().equals("(")) {
//如果栈顶为“(”,运算符入栈
stack.push(token);
}else{
//否则先把栈顶元素移除,加到列表中,再将运算符入栈
list.add(stack.pop());
stack.push(token);
}
}else {
//若栈为空,运算符入栈
stack.push(token);
}
}else if (token.equals("*") || token.equals("÷")){
//如果是“*”或“÷”,继续判断栈是否为空
if (!stack.empty()) {
//如果栈非空,判断栈顶元素是什么
if (stack.peek().equals("*") || stack.peek().equals("÷")) {
//如果栈顶为“*”或“÷”,先把栈顶元素移除,加到列表中,再将运算符入栈
list.add(stack.pop());
stack.push(token);
}else {
//如果栈顶为其他,运算符直接入栈
stack.push(token);
}
}else {
//如果栈为空,运算符直接入栈
stack.push(token);
}
} else if (token.equals(")")) {
//如果遇到“)”,开始循环
while (true) {
//先把栈顶元素移除并赋给A
String A = stack.pop();
if (!A.equals("(")) {
//如果A不为“(”,则加到列表
list.add(A);
} else {
//如果A为“(”,退出循环
break;
}
}
}else {
//如果为操作数,进入列表
list.add(token);
}
}
while (!stack.empty()) {
//将栈中元素取出,加到列表中,直到栈为空
list.add(stack.pop());
}
ListIterator<String> li = list.listIterator();//返回此列表元素的列表迭代器(按适当顺序)。
while (li.hasNext()) {
//将迭代器中的元素依次取出,并加上空格作为分隔符
Message += li.next() + " ";
li.remove();
}
message = Message;
}
四、测试方法
NifixToSuffer
类方法的测试(中缀转后缀)
Calculator
类方法的测试(四则运算)
RationalNumber
类方法的测试(有理数及真分数的计算)
五、运行过程截图
六、代码托管
七、遇到的困难及解决方法
-
Q1:在计算统计正确率时,输出正确率都是0%,如下图:
-
解决方法:仔细查看代码后,我们发现,在进行小数运算的时候,没有将int型的j转换为double型,导致只要不是100%,在进行小数运算时计算结果都会是0%
-
Q2:在编写
Calculator
类方法的测试代码时,遇到了Expected
与Actual
表达式相同,但测试失败的问题,如图:
-
解决方法:在我们多次观察产品代码,并查阅相关资料,参考Junit单元测试框架的使用中的提示,我们意识到可能是
Expected
和Actual
的类型不同,查看代码得知,Expected
为String
类型,而Actual
为RationalNumber
类型。我们调用RationalNumber
类中的toString
方法,将RationalNumber
类型转换为String
类型,问题得以解决。
-
Q3:在编写
RationalNumber
类的测试方法时,出现了与Q2相似的问题,如下图:
- 解决方法:有了上面的经验,我们意识到问题应该也和上面类似,查阅代码后发现果然如此,
Expected
为String
类型,而Actual
为int
类型,将String
类型转换为类型int
型就可以解决了。
八、对结对的小伙伴做出评价
在结对编程中,我承担驾驶员的角色,我的队友是领航员的角色,我主要负责代码的输入,如代码文件的创建及配置、UML图的制作等,我的队友主要起到引领的作用,由于我们对基础知识都掌握的不是很牢固,想实现一些功能时,总是不知道使用什么方法,这时候队友的工作就很起作用,她在旁边搜查相关知识点的资料,告诉我下一步要实现什么功能,然后我们一起讨论解决过程中遇到的问题。相对比第一次一起写代码,我们的默契度有了一定的提高,但是有时候想问题,思想还是不会同步,但是我相信我们的默契度以后还会得到更大的提高。
九、参考代码
由于对堆栈以及中缀转后缀不是很了解,我和我的小组成员参考了逆波兰算法实现四则运算,仔细理解这个代码之后,将NifixToSuffix
写出。
十、PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 30 | 50 |
Development | 开发 | ||
·Analysis | ·需求分析(包括学习新技术) | 60 | 100 |
·Design Spec | ·生成设计文档 | 50 | 60 |
·Disign Review | ·设计复审(和同事审核设计文档) | 20 | 40 |
·Code Standard | ·代码规范 | 30 | 40 |
·Design | ·具体设计 | 40 | 80 |
·Coding | ·具体编码 | 60 | 140 |
·Code Review | ·代码复审 | 30 | 30 |
·Test | ·测试(自我测试,修改代码,提交修改) | 30 | 90 |
Reporting | 报告 | ||
·Test Report | ·测试报告 | 60 | 100 |
·Size Measurement | ·计算工作量 | 30 | 30 |
·Postmortem&Process Improvement Plan | ·事后总结,并提出过程改进计划 | 30 | 50 |
合计 | 470 | 800 |
参考资料
2017-2018-2 165X 『Java程序设计』课程 结对编程练习_四则运算
结对编程项目-四则运算
现代软件工程讲义 个人项目和结对项目练习 四则运算
逆波兰算法实现四则运算