结对作业-四则运算计算器
个人项目
| 软件工程 | <网工1934> |
|---|---|
| 作业要求 | <作业要求连接> |
| 作业目标:完成四则运算计算器,实现并经过测试后上传到GitHub | <Github连接> |
| 队员:李文锋3119005379 李金锋3119005378 |
PSP表格
| PSP | Personal SoftwareProcess Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 35 |
| Estimate | 估计这个任务需要多少时间 | 80 | 100 |
| Development | 开发 | 2400 | 2800 |
| Analysis | 需求分析(包括学习新技术) | 300 | 350 |
| Design Spec | 生成设计文档 | 80 | 60 |
| Design Review | 设计复审 | 30 | 30 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
| Design | 具体设计 | 120 | 100 |
| Coding | 具体编码 | 300 | 420 |
| Code Review | 代码复审 | 60 | 60 |
| Test | 测试(自我测试,修改代码,提交修改) | 180 | 280 |
| Reporting | 报告 | 240 | 300 |
| Test Report | 测试报告 | 60 | 80 |
| Size Measurement | 计算工作量 | 60 | 60 |
| Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 120 | 120 |
| 合计 | 4090 | 4790 |
需求分析
一个具有题目和其答案生成且能检查题目与答案是否正确的计算器
题目生成:
需要生成运算符在3个以内(包括3个)的计算式,并以中辍表达式的形式表达出来
答案生成:
答案要与题目一一对应
校验答案:
需要输入需要校验的两个文件的文件名用以校验
思路
随机生成一组含有n个数字和n-1个符号的中辍表达式
将中辍表达式转化成逆波兰式并进行计算
把中辍表达式和答案分别输出到Exercise.txt和Answer.txt两个文件中

部分关键代码
生成表达式
public static String expression(int r){//生成表达式
String str;
String ans;
Random rand = new Random();
do {
str = "";
int t = 0;
t = rand.nextInt(3) + 2;//式子中用于运算的数的个数
String[] number = new String[t];
String[] symbol = new String[t-1];
String[] expression = new String[4*t-3];//由于分数中/和运算中的/是一致的,为了区分运算符和操作数
// 将操作数和运算符用空格隔开
for(int i=0;i<t;i++){//生成式子中的数
number[i] = Num.produce(r);
}
for(int i=0;i<t-1;i++){//生成式子中的运算符
symbol[i] = Operaction.symbol();
}
for(int i =0;i<4*t-3;i++){
if(i%4==0) //字符中奇数位为符号(基0)
expression[i] = number[i/4];
else if(i%4==2)
expression[i] = symbol[(i+2)/4-1];
else
expression[i] = " ";
}
for(int i = 0;i<4*t-3;i++){
if(expression[i].contains("'"))
expression[i] = Num.transformF(expression[i]);
}
for(int i=0;i<4*t-3;i++){
str = str + expression[i];
}
ans = calculate.count(str);
}while(ans.contains("-"));
return str + "=" + ans;
}
中辍表达式转为逆波兰式
public static String creat(String str){//中辍表达式转后辍表达式,逆波兰算法
int tmp = 0;
String expression = "";//输出的式子
String[] str1 = str.split(" ");
String str2 = "";
for(int i = 0;i< str1.length;i++){
str2 += str1[i];
}
Stack<Character> symbolStack = new Stack<Character>();
for(int i=0;i<str2.length();i++){
char c = str2.charAt(i);
if(isNum(c)) {//如果是数字,直接加进表达式中
tmp = (c-'0');
expression += tmp;
}
else if(c == '/')
expression += c;
else if(c == '(')//如果是左括号,直接压入符号栈
symbolStack.push(c);
else if(c == ')'&& !symbolStack.empty()){//如果遇到右括号且符号栈不为空,那么把符号都添加到表达式中
while(symbolStack.peek()!='('){
expression += symbolStack.pop();
}
symbolStack.pop();
}else{//如果是非左/右括号的运算符
expression += " ";
if(!symbolStack.empty()){//如果符号栈不为空,将当前符号和符号栈栈顶的符号的优先度做对比,如果优先度高,则压入符号栈
if((priority(c)>priority(symbolStack.peek())))
symbolStack.push(c);
else{//如果优先度没有栈顶符号高,那么将符号栈里的符号都输出到表达式中,直到找到一个优先度低于当前符号的,再压入符号栈
while(!symbolStack.empty()){
if(priority(c)<=priority(symbolStack.peek())){
expression += symbolStack.pop();
expression += " ";
}
else
break;
}
symbolStack.push(c);
}
}
else
{//如果符号栈为空,直接压入符号栈
symbolStack.push(c);
}
}
}
while(!symbolStack.empty()){//如果遍历完整个表达式后,扔存在运算符,则将符号都输出到表达式中
expression += " ";
expression += symbolStack.pop();
}
return expression;
}
计算
public static String count(String str1){
String str = Transform.creat(str1);
boolean haveNum = false;
boolean haveFraction = false;
int tmp = 0;
int numer = 0;
int gcd = 0;
char x;
Num a=null;
Num b=null;
Num c=null;
Stack<Num> number = new Stack<Num>();
for(int i=0;i<str.length();i++){
x = str.charAt(i);
if(Transform.isNum(x)){
haveNum = true;
tmp = tmp*10 + (x-'0');//每遇到一个数字就*10+x
}
else{
if(haveNum){//如果遇到数字后跟着符号的情况
haveNum = false;
if(x!='/'){//跟着的符号不为‘/’,即+。-。*、÷和空格的情况
if(haveFraction){//如果判定前面的数字为分数
Num fraction = new Num(numer,tmp);//创建分数并压入栈
number.push(fraction);
tmp = 0;
}else {//整数情况
Num interger = new Num(tmp,1);//把整数压入栈
number.push(interger);
tmp = 0;
}
}else{//数字后跟着的符号为/,即前面数字为分子的情况
haveFraction = true;
numer = tmp;//tmp是分子
tmp = 0;
continue;
}
}//如果跟着的符号是+。-。*、÷和空格的情况,压出栈中的数字并进行计算
if(x == '+'){
b = number.peek();
number.pop();
a = number.peek();
number.pop();
c = add(a,b);
}
else if(x == '-'){
b = number.peek();
number.pop();
a = number.peek();
number.pop();
c = subtract(a,b);
}
else if(x == '×'){
b = number.peek();
number.pop();
a = number.peek();
number.pop();
c = mul(a,b);
}
else if(x == '÷'){
b = number.peek();
number.pop();
a = number.peek();
number.pop();
c = div(a,b);
}
else if(x == ' '){//如果跟着的符号是空格
if(haveFraction)
haveFraction = false;
continue;
}
number.push(c);//把运算结果压入栈,进行下一次的运算
}
}
生成表达式并输出到文件中
public static void io(int n,int r)throws Exception{
FileWriter fw = new FileWriter("./Exercises.txt");
BufferedWriter Exercises = new BufferedWriter(fw);
FileWriter fw1 = new FileWriter("./Answer.txt");
BufferedWriter Answer = new BufferedWriter(fw1);
Map<String, Set<Integer>> map = new HashMap<String, Set<Integer>>();
String[] exercises = new String[n];
String[] answers = new String[n];
for(int i=0;i<n;i++) {//生成式子和答案并写入当前目录的文件中,生成n个
String[] output = Operaction.expression(r).split("=");//output[0]是式子 output[1]是答案
if(map.containsKey(output[1])){
Set<Integer> set = map.get(output[1]);
while (set.contains(output[0].length()))
output = Operaction.expression(r).split("=");
set.add(output[0].length());
}else {
map.put(output[1],new HashSet<Integer>(Arrays.asList(output[0].length())));
}
exercises[i] = output[0];
answers[i] = output[1];
Exercises.write(i+1 + ". " + output[0]);
Exercises.newLine();
Exercises.flush();
Answer.write(i+1 + ". " + calculate.count(output[0]));
Answer.newLine();
Answer.flush();
}
Exercises.close();
Answer.close();
}
测试


修改其中一题的答案再校验



小结
李文锋:在这个项目中,第一次接触两个人的合作。在这个过程中,两个人先讨论了计算器的一个大题架构,然后逐步细化每一个部分要做什么,再进行代码实现和审计工作。
李金锋:这是我第一次与别人合作开发,在这次项目中培养了我的团队协作能力和与他人沟通交流的能力,也提高了我的编程能力,项目过程中有时与伙伴的交流会解决我苦思的问题,合作提高了效率。

浙公网安备 33010602011771号