本项目Github地址:https://github.com/Rollsom/MyApp
项目简介:
实现一个自动生成小学四则运算题目的命令行程序。
项目相关要求:
使用 -n 参数控制生成题目的个数
使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如:Myapp.exe -r 10
将生成10以内(不包括10)的四则运算题目。该参数可以设置为1或其他自然数。该参数必须给定,否则程序报错并给出帮助信息。
3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
4. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
5. 每道题目中出现的运算符个数不超过3个。
6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
1. 四则运算题目1
2. 四则运算题目2
……
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8。
7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
1. 答案1
2. 答案2
8. 程序应能支持一万道题目的生成。
9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
Myapp.exe -e
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
解题思路:
大致思路为:先随机生成若干个子表达式(表达式内操作数不超过2个,若操作数为2个则加上括号),根据数字特征生成合适的操作符,随后根据子表达式的总值来生成表达式之间的运算符号,期间去除多余的括号,并且将小数换成真分数。从而生成一个符合要求的表达式。
查重则使用二叉树,在生成子表达式的过程中构建二叉树,查重就变成了两个二叉树是否相等的问题。
最后输出文件以及正误判断只需要用到文件的输入输出流以及简单的字符串判断方法即可。
代码结构:
Expression类:当做数据结构来用,里面主要设置一些表达式里面的一些参数方便编程。
Data类:数据的属性,主要用来处理真分数的转换,整数及真分数的运算,生成符合输入输出的数字表达。
CQ类:生成子表达式及组合表达式,生成结果。
ExpressionArray类:用来处理生成的表达式数组,主要用来查重以及存储生成的每一个表达式。
Tree类:子表达式附加的类,用来辅助查重。
Saveanswer类:将生成的表达式输出到文件中,以及统计正误数量。
关键代码说明:
生成子表达式:
public void CQQ(Expression e,int Range) {//创造表达式 e.flag1 = true; Random r = new Random(); if(this.CurrentOperateNum==3||this.CurrentOperateNum==0) { e.Operate1.CreatNumble(Range); this.CurrentOperateNum += 1; e.value = e.Operate1.ExpressionValue; e.Result = e.Operate1; this.ExpressionNum += 1; e.flag1=true; e.Node.value = e.Operate1.ExpressionValue; } else if(this.CurrentOperateNum==4) return ; else { e.Operate1.CreatNumble(Range); this.ExpressionNum += 1; e.flag1 = true; if(r.nextInt(2)==1) //生成两个操作数 { e.flag = true; e.Operate2.CreatNumble(Range); this.CurrentOperateNum += 2; switch(r.nextInt(4)) { case 0 : e.value = e.Operate1.ExpressionValue + e.Operate2.ExpressionValue; e.Operator = '+'; break; case 1 : while(e.Operate1.ExpressionValue < e.Operate2.ExpressionValue) { e.Operate1.CreatNumble(Range); e.Operate2.CreatNumble(Range); } e.value = e.Operate1.ExpressionValue - e.Operate2.ExpressionValue; e.Operator = '-'; break; case 2 : e.value = e.Operate1.ExpressionValue * e.Operate2.ExpressionValue; e.Operator = '*'; break; case 3 : e.value = e.Operate1.ExpressionValue / e.Operate2.ExpressionValue; e.Operator = '÷'; break; } e.Result = e.Result.Carculate(e, e.Operate1, e.Operate2, e.Operator); e.Node.e1.value = e.Operate1.ExpressionValue; e.Node.e2.value = e.Operate2.ExpressionValue; e.Node.value = e.value; } else { this.CurrentOperateNum += 1; e.value = e.Operate1.ExpressionValue; e.Result = e.Operate1; e.Node.value = e.Operate1.ExpressionValue; } }
组建已经生成的表达式:
public String GroupSonExpression(Expression e1,Expression e2,Expression father) { int Operate; Random r = new Random(); Operate = r.nextInt(4); while((e1.value)) Operate = r.nextInt(3); if(e1.flag1==true&&e2.flag1==true) { switch(Operate) { case 0 : father.Operator = '+';father.value = e1.value + e2.value; if(e1.flag==true) { e1.flag = false; e1.GetExpression(); } if(e2.Operator=='*'||e2.Operator=='÷') { e2.flag = false; e2.GetExpression(); } break; case 1 : father.Operator = '-';father.value = e1.value - e2.value; if(e1.flag==true) { e1.flag = false; e1.GetExpression(); } if(e2.Operator=='*'||e2.Operator=='÷') { e2.flag = false; e2.GetExpression(); } break; case 2 : father.Operator = '*';father.value = e1.value * e2.value; if(e1.Operator=='*'||e1.Operator=='÷') {e1.flag = false; e1.GetExpression();} break; case 3 :father.Operator = '÷';father.value = e1.value / e2.value; if(e1.Operator=='*'||e1.Operator=='÷') {e1.flag = false; e1.GetExpression();} break; } father.Result = father.Result.Carculate(father, e1.Result, e2.Result, father.Operator); father.SonExpression = e1.Expression + father.Operator + e2.Expression; father.Expression = "("+ father.SonExpression +")"; father.flag1 = true; father.Node.value = father.value; father.Node.e1 = e1.Node; father.Node.e2 = e2.Node; } else if(e2.flag1==false&&e1.flag1==true) { father.value = e1.value; father.Operate1 = e1.Operate1; father.Operate2 = e1.Operate2; father.Expression = e1.Operate1.Fraction; father.Operator = e1.Operator; father.Result = e1.Result; father.flag = e1.flag; father.Result.ExpressionValue = e1.Result.ExpressionValue; father.flag1 = true; if(e1.Operator==' ') father.SonExpression = e1.Expression; else father.SonExpression = e1.Operate1.Fraction+ father.Operator + e1.Operate2.Fraction; father.Node = e1.Node; } else father = e2; return father.Expression; }
真分数的各种运算:
public Data Carculate(Expression e,Data d1,Data d2,char Operator) { Data d = new Data(); switch(Operator) { case '+':d.Molecule = d1.Molecule * d2.Deno + d2.Molecule * d1.Deno; d.Deno = d1.Deno * d2.Deno; d.integer = d1.integer + d2.integer; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; case '-': d.Molecule = d2.Deno * (d1.integer * d1.Deno+d1.Molecule) - d1.Deno * (d2.integer * d2.Deno + d2.Molecule); //d1.Molecule * d2.Deno - d2.Molecule * d1.Deno; d.Deno = d1.Deno * d2.Deno; d.integer = 0; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; case '*': d.Molecule = (d1.Molecule+d1.integer * d1.Deno) * (d2.Molecule + d2.integer * d2.Deno); d.Deno = d1.Deno * d2.Deno; d.integer = 0; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; case '÷':d.Molecule = (d1.Molecule+d1.integer * d1.Deno) * d2.Deno; d.Deno = d1.Deno * (d2.Molecule + d2.integer * d2.Deno); d.integer = 0; d.Simplication(); d.ExpressionValue = (double)d.integer+(double)(d.Molecule)/(double)d.Deno; d.CreatFractor(); e.valueExpression = d.Fraction; break; default : System.out.println("计算出错");break; } return d; }
查重:
boolean IFequal(Tree T1,Tree T2) { if(T1==null&&T2==null) return true; else if(T1==null||T2==null) return false; else if(T1.value!=T2.value||T1.Operator!=T2.Operator) return false; else if((IFequal(T1.e1,T2.e1)&&IFequal(T1.e2,T2.e2))||(IFequal(T1.e2,T2.e1)&&IFequal(T1.e1,T2.e2))) return true; return false; }
运行测试举例:
生成10000条表达式并且操作数范围为20,输出到.txt文件上面。
测试比较答案对错:
psp:
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 |
120 |
· Estimate |
· 估计这个任务需要多少时间 |
60 |
120 |
Development |
开发 |
1800 |
2140 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
60 |
· Design Spec |
· 生成设计文档 |
60 |
90 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
20 |
30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
40 |
40 |
· Design |
· 具体设计 |
120 |
180 |
· Coding |
· 具体编码 |
720 |
960 |
· Code Review |
· 代码复审 |
60 |
60 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
720 |
720 |
Reporting |
报告 |
40 |
40 |
· Test Report |
· 测试报告 |
15 |
15 |
· Size Measurement |
· 计算工作量 |
0 |
0 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
25 |
25 |
合计 |
|
1900 |
2300 |
代码覆盖率:
执行-n 与 -r 时:
执行-e 与 -a 时:
项目总结:
本次项目比上一次的项目的难度要高许多,制作时间比较紧张。在这一次编程中,我实现功能需要用到的类比较多,类比较简单所以多而且繁琐,相互又有联系,所以出bug的时候总是会卡很久。而且这次随我个人而言也是一次很大的挑战,我平时没有学习技术,只有要做项目的时候才去学,是非常痛苦的事情,这痛苦的经历也告诉了我平时练习的重要性。。最主要的是,这一次是结对编程,两个人在一起编写程序,相互提出想法,轻松解决小错误,有小的问题与失误常常能够及时发现,避免了到时候重新观看代码耗费大量时间,是一次非常好的体验。两人相互督促,效率比较高,虽然没有一个人打码那么自在,但是确实感受到了结对编程的好处。
评价:
我的队友梁汉烽在我打码的时候给了我很多的帮助,他常常坐在我旁边看我打码,在旁边给出他的思路,人也很幽默,没有头绪的时候也会说一些骚话活跃气氛。我从他那里学到了不少东西,他指导我代码的规范性,让我对设计程序时的结构性有了进一步的了解,而且经常提醒我犯得一些小错误。
罗彬进行了对功能的基本架构,并且提供了基本思路,在此过程中我为他提供意见,尽可能的求同存异,改善基本情况,两个人同时进行编码可以在编码过程中看出错误,避免BUG的过多产生,导致任务进度的减慢。