四则运算计算器
课程 | 软件工程 |
---|---|
要求 | 结对项目作业 |
题目 | 四则运算生成器 |
成员 | 学号 |
---|---|
陈树东 | 3119005407 |
冯波昌 | 3119005410 |
GitHub地址
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 40 |
Estimate | 估计这个任务需要多少时间 | 2000 | 2560 |
Development | 开发 | 1200 | 1350 |
Analysis | 需求分析 (包括学习新技术) | 60 | 130 |
Design Spec | 生成设计文档 | 20 | 30 |
Design Review | 设计复审 (和同事审核设计文档) | 30 | 15 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 10 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 30 | 60 |
Code Review | 代码复审 | 15 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 500 | 780 |
Reporting | 报告 | 30 | 60 |
Test Report | 测试报告 | 30 | 15 |
Size Measurement | 计算工作量 | 15 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 60 |
合计 | 5050 | 5185 |
一、题目要求
1.对自然数以及真分数进行四则运算
2.用 -n 参数控制生成题目个数 -r 参数控制题目中数值范围
3.运算过程不能产生负数
4.生成的题目中如果存在形如e1÷ e2的子表达式结果是真分数
5.不超过三个运算符
6.程序一次运行过程中,题目不能重复
7.在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件
8.程序应能支持一万道题目的生成。
9.程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计
二、性能分析
三、工程结构及代码覆盖率
四、关键代码
检查 CheckGrade:
while((an=BRanswer.readLine())!=null) {
ex=BRexersise.readLine();
int point=an.indexOf(".");//找到"."所在处,因为答题格式和答案格式均为No.Answer,只需要比较“."后的答案即可
String stran=an.substring(point+1);//取答案
stran=stran.trim();
String strex=ex.substring(point+1);//取标准答案
strex=strex.trim();
if(stran.equals(strex)) {
//答案和标准答案相同时,将题号压入Correct栈
String wno=an.substring(0, point);
Wrong.add(wno);
}else {
//答案和标准答案不同时,将题号压入Wrong栈
String cno=an.substring(0, point);
Correct.add(cno);
}
}
中缀表达式转换成后缀表达式:
/**
*将中缀表达式转换为后缀表达式入栈
* @param infix
* retrun postfix
*/
public static List<String> TransferToPostfix(List<String> infix){
List<String> postfix=new ArrayList<>();
Stack<String> s1= new Stack<>();
for (String str : infix) {
if(str.equals("(")) {
s1.push(str);
}//若是左括号,进栈
else if(str.equals(")")) {
while(!s1.peek().equals("(")) {
postfix.add(s1.pop());
}//若是右括号,将栈内内容出栈直到遇到左括号为止
s1.pop();
}
else if(str.equals("+")||str.equals("-")||str.equals("×")||str.equals("÷")) {//遇到运算符时
while (s1.size() != 0 && getValue(s1.peek()) >= getValue(str)) {
postfix.add(s1.pop());
}//栈非空,从栈中弹出元素直到遇到更低级的运算符/栈空,再入栈
s1.push(str);
} else {
postfix.add(str);
}//栈空,直接入栈
}
while (s1.size() != 0) {
postfix.add(s1.pop());
}//将栈内所有元素弹出
return postfix;
}
查重表达式:
/**
*查重表达式的生成
*/
public List<String> getCheck(String str) {
List<String> linfix = transform.ToList(str);
List<String> lpostfix = calculates.TransferToPostfix(linfix);
List<String> cnkiexp = new ArrayList<String>();
Stack<String> st = new Stack<String>();
String top1 ;
top1= "";
String top2 ;
top2= "";
for (String kk : lpostfix) {
if (!calculates.IsOperator(kk)) {//不是运算符时
st.push(kk);//压入栈
} else if (kk.equals("+") || kk.equals("-") || kk.equals("×")) {//是运算符
cnkiexp.add(kk);
top1 = st.pop();
top2 = st.pop();
if (top2 != "¥") {
cnkiexp.add(top2);
}
if (top1 != "¥") {
cnkiexp.add(top1);
}
st.push("¥");
} else if (kk.equals("÷")) {
cnkiexp.add(kk);
top1 = st.pop();
top2 = st.pop();
cnkiexp.add(top2);
cnkiexp.add(top1);
st.push("¥");
}
}
return cnkiexp;
}
五、设计实现过程
1.两个思路
一开始没有什么思路,在请教了同学之后,他们提供了几个思路给我:
①将四则运算表达式存储在二叉树中,中序遍历还原成四则运算表达式
②利用栈,转换成后缀表达式 如:a + b*c + (d * e + f) * g → a b c * + d e * f + g * +
在了解到 JAVA本身有stack类再加上我们递归以及二叉树学的不好,果断选择了用线性表来实现。
具体的过程如下:
1)遇到操作数,直接输出。
2)遇到操作符,将其放入到栈中,遇到左括号也将其放入栈中。
3)直到遇到一个右括号,将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。
4)遇到任何其他的操作符,如加减乘除运算符等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。
注意:只有在遇到" ) "的情况下才弹出" ( ",其他情况都不会弹出" ( "。
5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
2.表达式查重
只有加法乘法有交换律,所以只需要对这两个进行特别判断
后缀表达式存进进线性表1中
线性表1作为key值存入哈希表
将“+”,“ * ”号两边的运算数交换存入线性表2
判断两线性表是否相同即可;
3.运行结果
10000道小于10的运算:
10道小于10的运算:
六、总结
陈树东:
1.这次项目是结对项目,需要两个队友相互间的配合,也让我们学会了如何合作完成项目
2.一开始我们完全没有头绪,我们先考虑需要有多少个需求,然后考虑用什么方法,最后才开始动手写程序
3这次对于我们两个都是一个不小的挑战,经过这次项目,我们更加了解到团队项目的流程与分工
冯波昌:
1.结对项目不再是单枪匹马莽着来了,两个人前期的沟通很重要。
2.双方罗列出各自的思路,再一起讨论确定最终的方向,比一个人想方法,简单高效很多。
3.两个人对题目的理解能互相激励对方的思路,不同功能的实现有不同的方法,这里需要大家一起确定方向。
4.在碰到解决不来的问题时,及时的请教同学,能更好的找到正确的思路。