20165214 第一次结队编程项目——四则运算第二周
需求分析
本周的结队编程想要实现一个四则运算系统,它可以自动生成n个计算题(本周不包括分数),其中n由我们输入。每输出一道题目,运行程序的人需要输入相应的答案,直到最后一道题做完。最后,统计正确率。然后,在这个基础上可以进行相应的功能扩展,比如语言支持等。
设计思路
我需要在上周的基础上对程序进行补充。我觉得这个程序关键就分成两大部分,第一部分是题目的随机生成,第二部分是算式的运算。其他的各种扩展在这两个步骤完成后再来添加即可。
于是我们开始先着手于随机生成,然后着手算式运算,接着把他们拼在一起,然后再进行各种扩展项目的补充。这是我们设计的总思路。
随机生成题目时,我们的想法是,利用随机数random,根据随机数生成的数来对应符号与数字,从而形成算式。
运算算式时,我们参娄老师的博客2016-2017-2 《Java 程序设计》课堂实践项目——数据结构应用中的栈的思想来解决,真分数的运算参考课本中第四章的例子22。
本周达成:
①能够随机生成n道题目,n由我们输入,最大长度可直接在程序里面修改;
②支持真分数运算,支持多运算符;
③能够计算正确率并且按照百分比形式输出,取到小数点后一位。
④支持简体中文、繁体中文、英语;
⑤能够选择参与运算的数字的最大值;
⑥能够查重到级别0(看两个问题序列是否相等,由于有的生产的算式很长,不知道怎么查重比较好);
⑦将生成的算式都写入文档中。
关键代码解释
由于生成随机算式的代码是我个人的一些想法,,所以可能会比较不好理解,于是特别提出
File file=new File("Question.txt");
GetNumber GN = new GetNumber();
GetOperation GO = new GetOperation();
GetBracket GB = new GetBracket();
Random random = new Random();
PraticeSystem ps=new PraticeSystem();
int language;
Scanner scanner=new Scanner(System.in);
int range;
GetQuestion(int language){
switch (language) {
case 0:
System.out.println("选择运算数最大为:");
range=scanner.nextInt();//获得四则运算中数字的上限。
break;
case 1:
System.out.println("Choose The maximum number of operands is");
range=scanner.nextInt();//获得四则运算中数字的上限。
break;
case 2:
System.out.println("選擇運算數最大為");
range=scanner.nextInt();//获得四则运算中数字的上限。
break;
}
}
public String get(){
int flag=0,flag2=0;
String Question="";
for (int j = 0; j < (random.nextInt(7) + 2) * 2 - 1; j++) { //这样能够保证长度在一定范围内随机,并且长度>=3
if (j % 2 == 0) {
int choose=random.nextInt(2);//括号和整数只能在奇数位,除非是括号内的整数才能在偶数位
if(choose==0){ //这里使得生成括号和整数都是随机的
Question = Question + GN.A(range); //生成整数连接在问题的后面
}
else{
if(flag==0){ //这里flag作为标记,因为括号要按顺序来,一旦产生左括号,后面在产生括号时一定要产生右括号。
Question = Question + GB.A(flag); //左括号的右边一定紧跟数字
Question = Question + GN.A(range);
flag=1; //之后再产生括号就产生右括号
}
else if(flag==1){
Question = Question + GN.A(range);//右括号的左边一定紧跟数字
Question = Question + GB.A(flag);
flag=0;
}
}
}
else if (j % 2 == 1){
char c=GO.A(flag2);
Question = Question + c;
if(c==\'/\'){ //这里是因为不产生歧义,因为在数学表达式中,如果有a/b/c,那么究竟是a除以b/c还是a/b除以c?
//而÷不会产生这个问题。
flag2=1;
}
else{
flag2=0;
}
}
}
int len = Question.length();
char a = Question.charAt(len - 1);
if (a == \'+\' || a == \'-\' || a == \'*\'|| a == \'/\'|| a == \'÷\') { //如果最后一个字符是运算符,那么舍弃这个运算符。
Question = Question.substring(0, len - 1);
}
len = Question.length();
a = Question.charAt(len - 1);
if (flag==1) {
Question = Question+\')\'; //如果到最后都没有产生右括号,而在之前已经产生了左括号,那么要补上右括号
flag=0;
}
len = Question.length();
a = Question.charAt(len - 1);
char b=Question.charAt(0);
int count=0;
for(int h=0;h<len;h++){
char c=Question.charAt(h);//如果一个算式只有一个左括号和一个右括号且这两个括号分别在算式的两边,则舍弃。
if(c==\'(\'||c==\')\'){
count++;
}
}
if(count==2){
if(a==\')\'&&b==\'(\'){
Question = Question.substring(1, len - 1);
}
}
Question = Question.replaceAll("\\\\([0-9]+\\\\)",Integer.toString(random.nextInt(10)+1));
//利用正则表达式替换在字符串中像(1)这样的情况。
try{
FileWriter tofile=new FileWriter(file,true);
BufferedWriter out= new BufferedWriter(tofile);
out.write(Question);
out.newLine();
out.close();
}
catch (IOException e){}
return Question;
真分数(题目生成/题目运算判题)
运行截图:
码云链接
其中关键代码的解释我们注释在代码中,码云里有体现~
JUnit测试
运算的测试:
后缀表达式的测试:
UML图
PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 120 | 100 |
· Estimate | · 估计这个任务需要多少时间 | 600 | 580 |
Development | 开发 | 60 | 60 |
· Analysis | · 需求分析 (包括学习新技术) | 5 | 5 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 40 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 60 |
· Design | · 具体设计 | 30 | 35 |
· Coding | · 具体编码 | 120 | 120 |
· Code Review | · 代码复审 | 10 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 100 |
Reporting | 报告 | 30 | 40 |
· Test Report | · 测试报告 | 10 | 5 |
· Size Measurement | · 计算工作量 | 20 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 10 |
合计 | 1235 | 1145 |
遇到的困难与解决方法
在编程时遇到的一些关于类的方法的问题我都用API解决了,这里总结一下其他的问题~
- 问题一:在随机生成题目时,会出现单个数字字符但是有括号的现象,如"(1)",这时候我想用正则表达式,使用String类的repalceAll()方法来覆盖这种情况。但是,我发现要用正则表达式来匹配"([0-9]+)"老是失败,程序直接把引号里面的字符串作为匹配的对象,而没有发挥[]和+的作用。
- 问题一解决方案:在不断地摸索之下,我忽然想到,会不会是因为在正则表达式内使用括号需要用转义符号来表示呢?于是我尝试着在括号的前面加上了\\,如图:
然后发现成功了。
- 问题二:在测试运算的时候,如果有两个等级相等的操作符连着使用的时候,会先算左边的再算右边的。这是不符合我们日常生活中的计算的。
- 问题二解决方案:仔细检查了代码过后发现是在压栈这一步骤中,对运算符的优先级比较出现了错误。遇到运算符时,当比栈顶的运算符优先级小时,压栈。我写成了小于等于,出现了这个问题。
点评伙伴
本周的合作是基于上周的基础上的。随着合作的次数增加,我和队友的配合也越来越好了,因此结队编程的效率有了不少提升,关键的一点是现在两个人如果意见上有什么不统一也比较敢于发表出来,这样很有利于我们的思考。
这次我的队友表现也很好能和我共同学习一起进步希望今后能够一直合作下去。
结队编程照片: