结对项目——自动生成小学四则运算题目
1.Github项目地址:https://github.com/13202008832/FourArithmetic
作者:陈惠霖 3118005088
侯晓龙 3118005091
2.PSP表格(时间评估)
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | |
· Estimate | · 估计这个任务需要多少时间 | 20 | |
Development | 开发 | 840 | |
· Analysis | · 需求分析 (包括学习新技术) | 120 | |
· Design Spec | · 生成设计文档 | 40 | |
· Design Review | · 设计复审 (和同事审核设计文档) | 20 | |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | |
· Design | · 具体设计 | 60 | |
· Coding | · 具体编码 | 400 | |
· Code Review | · 代码复审 | 30 | |
· Test | · 测试(自我测试,修改代码,提交修改) | 150 | |
Reporting | 报告 | 60 | |
· Test Report | · 测试报告 | 30 | |
· Size Measurement | · 计算工作量 | 20 | |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | |
合计 | 920 |
3.效能分析
出现问题:因为我们使用的是栈操作,不同类型的经常要调用函数进行转变,又重新转换回去
解决方案:全部转换为同一类型操作,减少类型转变
消耗最大函数:表达式生成函数
void Exp(stack S,int I) { //提取符号 String f = new String(); f = S.pop(); //定义优先等级 int i; if (f == "+" || f == "-") i = 1; else i = 2; //提取右表达式 stack b = new stack(7); { int m = 0, n = 0; while(true) { String x = S.pop(); if (x == "+" || x == "-" || x == "×" || x == "÷") n++; else m++; b.push(x); if(1 == m - n) break; } b.inversion(); } //提取左表达式 stack a = new stack(7); while(true) { String x = S.pop(); a.push(x); if(S.isEmpty()) break; } a.inversion(); //表达式输出 if(i < I) Y = Y + "("; if(a.top == 0) Y = Y + a.pop(); else Exp(a,i); Y = Y + " "+f+" "; if(b.top == 0) Y = Y + b.pop(); else Exp(b,i+1); if(i < I) Y = Y + ")"; if(I == 0) Y = Y + " = "; }
4.设计实现过程
通过对题目的分析,我们发现对于分数和整数的表示都可以用分数来表示,于是我们创建了一个Fraction的实体类,并使用Method包下的Rando类中的getFraction(intmax)方法随机获得一个String类型的分数,和getOperator()方法来获取一个随机的操作符,对于表达式的生成,我们采用了逆波兰表达式,并用Entity包下的stack类存储和Method包下的rpn类排序入栈,answer类来实现表达式答案的获取,而对于实现四则运算的加减乘除,我们在Method包下建立了OpFraction类,用 opFr(String m,String op, String n) 方法实现,最后,我们采用的是建立JavaWeb项目来完成这个结对项目,用户可以在jsp建立的页面上控制题目数量和数值范围,再在D盘中3个txt文件中查看题目、答案、答题情况。
5.代码说明
Fraction实体类代码
package Entity; import Method.OpFraction; public class Fraction { private int fson; //分子m private int fma; //分母n private String fract; //分数m/n private String daiFract; //带分数i'm/n private int dai; //i public int getDai() { return dai; } public void setDai(int dai) { this.dai = dai; } public String getDaiFract() { return daiFract; } public void setDaiFract(int i,int m,int n) { this.dai=i; this.fma=n; this.fson=m; this.daiFract = String.valueOf(i)+"'"+String.valueOf(m)+"/"+String.valueOf(n);; } public int getFson() { return fson; } public void setFson(int fson) { this.fson = fson; } public int getFma() { return fma; } public void setFma(int fma) { this.fma = fma; } public String getFract() { return fract; } public void setFract(int m,int n) { this.fma=n; this.fson=m; this.fract = String.valueOf(m)+"/"+String.valueOf(n); } public String tranFr(Fraction q) { int max; OpFraction o=new OpFraction(); if(q.fson==0) {return "0";}else { max=o.maxNum(q.fma,q.fson); q.setDaiFract(q.dai, q.fson/max, q.fma/max); if(q.dai==0) { if(q.fma==1) { return String.valueOf(q.fson); } else{ return String.valueOf(q.fson)+"/"+String.valueOf(q.fma); }} else { if(q.fma==1) { return String.valueOf(q.fson); } else return String.valueOf(q.dai)+"'"+String.valueOf(q.fson)+"/"+String.valueOf(q.fma); } }} public double val(Fraction f) { if(f.fson==0)return 0; return f.dai+f.fson/f.fma; } }
getFraction(intmax)方法随机获得一个String类型的分数
public String getFraction(double max) { int dai,son,ma; Fraction f=new Fraction(); do { dai=getNumber((int) max); son=getNumber((int) max); ma=getNumber((int) max); for(;ma==0;) ma=getNumber((int) max); f.setDaiFract(dai, son, ma); }while(!(f.val(f)ma); return f.tranFr(f); }
tranFr(Fraction f)将Fraction转换为String形式
public String tranFr(Fraction q) { int max; OpFraction o=new OpFraction(); if(q.fson==0) {return "0";}else { max=o.maxNum(q.fma,q.fson); q.setDaiFract(q.dai, q.fson/max, q.fma/max); if(q.dai==0) { if(q.fma==1) { return String.valueOf(q.fson); } else{ return String.valueOf(q.fson)+"/"+String.valueOf(q.fma); }} else { if(q.fma==1) { return String.valueOf(q.fson); } else return String.valueOf(q.dai)+"'"+String.valueOf(q.fson)+"/"+String.valueOf(q.fma); } }}
tranString(String str)将String类型用Fraction存储
Fraction tranString(String str) { Fraction f=new Fraction(); if(str.indexOf("'")>=0) { String[] s1=str.split("'"); String[] s2=s1[1].split("/"); f.setDaiFract(Integer.parseInt(s1[0]),Integer.parseInt(s2[0]), Integer.parseInt(s2[1])); } else { if(str.indexOf("/")>=0) { String[] s1=str.split("/"); f.setDaiFract(0,Integer.parseInt(s1[0]), Integer.parseInt(s1[1])); } else { f.setDaiFract(0,Integer.parseInt(str),1); } } return f; }
rpn类实现将生成的数和操作符组成随机逆波兰表达式的功能
public class rpn { public stack Y = new stack(7);; public void r(int max) { Rando f=new Rando(); Y.push(f.getFraction(max)); Y.push(f.getFraction(max)); int m = 0,n = 0,i = (int)(Math.random()*3);//均等生成0、1、2 while(m < i && n < i) { int j = (int)(Math.random()*2); if(j == 0 || n-m>0) {Y.push(f.getFraction(max));m++;} else {Y.push(f.getOperator());n++;} } while(m == i && n < i) { Y.push(f.getOperator());n++; } while(n == i && m < i) { Y.push(f.getFraction(max));m++; } Y.push(f.getOperator()); } }
expression类实现了转换逆波兰表达式为标准表达式的功能
public class expression { public String Y = new String(); public stack as; public expression(stack As) { this.as=As; } void Exp(stack S,int I) { //提取符号 String f = new String(); f = S.pop(); //定义优先等级 int i; if (f == "+" || f == "-") i = 1; else i = 2; //提取右表达式 stack b = new stack(7); { int m = 0, n = 0; while(true) { String x = S.pop(); if (x == "+" || x == "-" || x == "×" || x == "÷") n++; else m++; b.push(x); if(1 == m - n) break; } b.inversion(); } //提取左表达式 stack a = new stack(7); while(true) { String x = S.pop(); a.push(x); if(S.isEmpty()) break; } a.inversion(); //表达式输出 if(i < I) Y = Y + "("; if(a.top == 0) Y = Y + a.pop(); else Exp(a,i); Y = Y + " "+f+" "; if(b.top == 0) Y = Y + b.pop(); else Exp(b,i+1); if(i < I) Y = Y + ")"; if(I == 0) Y = Y + " = "; } public void exp() { Exp(as,0); } }
answer类实现获取生成好的表达式的答案的功能
public class anwser { public String Y = new String(); public stack as; public boolean flag = true; public anwser(stack As) { this.as=As; } public void anw(){ stack p = new stack(7); while(as.top>0) { String a = new String(); String b = new String(); String f = new String(); f = as.pop(); int i = 0; //得出一组fab while(true) { String x; x = as.pop(); if (x == "+" || x == "-" || x == "×" || x == "÷") { p.push(f); if(i == 1) p.push(a); f = x; i = 0; } else if(i == 0) { a = x; i++; } else { b = x; i++; } if(i == 2)break; } String y = new String(); OpFraction op=new OpFraction(); y = op.opFr(b,f,a); if(y=="-1")flag=false; as.push(y); while(!p.isEmpty()) { as.push(p.pop()); } } Y = as.pop(); } }
DaoTxt类实现了将String类型数据写入txt的相关功能
package Method; import java.io.FileWriter; import java.io.IOException; public class DaoTxt { private static String answerPath = "D:\\answerfile.txt"; private static String correctPath = "D:\\Grade.txt"; private static String tiPath = "D:\\exercisefile.txt"; private static void saveAsFileWriter(String content,String path) { FileWriter fwriter = null; try { // true表示不覆盖原来的内容,而是加到文件的后面。若要覆盖原来的内容,直接省略这个参数就好 fwriter = new FileWriter(path, true); fwriter.write(content); } catch (IOException ex) { ex.printStackTrace(); } finally { try { fwriter.flush(); fwriter.close(); } catch (IOException ex) { ex.printStackTrace(); } } } public void clear(String path) { FileWriter fwriter = null; try { // true表示不覆盖原来的内容,而是加到文件的后面。若要覆盖原来的内容,直接省略这个参数就好 fwriter = new FileWriter(path, false); fwriter.write("\n"); } catch (IOException ex) { ex.printStackTrace(); } finally { try { fwriter.flush(); fwriter.close(); } catch (IOException ex) { ex.printStackTrace(); } } } public void saveAnswer(String content) { saveAsFileWriter(content,answerPath); } public void saveGrade(String content) { saveAsFileWriter(content,correctPath); } public void saveExercise(String content) { saveAsFileWriter(content,tiPath); } public void clearAll() { clear(answerPath); clear(correctPath); clear(tiPath); } }
6.测试说明
首先在eclipse中启动Tomcat服务(2000与10皆可自己输入)
点击确定,生成题目
点击提交
在exercisefile.txt中存放了生成的所有题目
在answerfile.txt中存入了所有题目以及答案
在Grade.txt中存储了答题情况
7.PSP表格(实际耗费时间)
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 30 |
Development | 开发 | 840 | 860 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 100 |
· Design Spec | · 生成设计文档 | 40 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 20 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
· Design | · 具体设计 | 60 | 60 |
· Coding | · 具体编码 | 400 | 360 |
· Code Review | · 代码复审 | 30 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 150 | 240 |
Reporting | 报告 | 60 | 70 |
· Test Report | · 测试报告 | 30 | 40 |
· Size Measurement | · 计算工作量 | 20 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 |
920 | 960 |
8.项目小结
项目总结:本次结对作业进行基本顺利,我们各自负责着自己的模块,虽然线上讨论减少了一些比较明确的沟通,但是通过对方的介绍跟对各自代码的微调,如特定函数的使用说明、格式的规定(我们最终统一将数字和符号改为String类型),使得我们最终顺利对接成功,经过统一整理后才做出这次项目。因此,对于结对项目,我们有以下观点:
- 合理分配工作,各自选择自己擅长的模块
- 确定好对接方式,有相应的函数调用说明
- 找出问题所在,积极与对面沟通解决方案
结对感受:陈惠霖:侯晓龙是个非常不错的伙伴,他帮我解决了很多我不擅长的模块,提供了我很多函数的调用,也很少有bug的存在,给予了我很多帮助,非常给力!
侯晓龙:两个人一起合作感觉还不错,只需要自己完成一部分,所以工作时压力不是很大,很多问题经过讨论就会迎刃而解,队友陈惠霖给了我很多帮助,感觉非常不错。