1、GitHub地址:https://github.com/3116004700/ruanjiangongcheng
2、项目需求:
- 生成的题目中计算过程不能产生负数(完成)
- 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。(完成)
- 程序一次运行生成的题目不能重复,生成的题目存入执行程序的当前目录下的Exercises.txt文件(完成)
- 每道题目中出现的运算符个数不超过3个(完成)
- 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件(完成)
- 程序应能支持一万道题目的生成。(完成)
- 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计。(完成)
3、解题思路描述:
拿到这个项目首先想到的是和上一个项目的区别,都会涉及到IO的输出,因为Java里面IO的相关方法使用起来比较简便,所以两次的实现语言都是Java。
这次的项目有几个要注意的地方:四则表达式的生成,要保证随机;项目要求结果不能为负数;要判断答案中的对错,并且统计数量。这些问题的解决办法会在后面详细说明,这里就不多赘述了。
4、设计实现过程:
整个项目总共有7个类,每个类里面有不同的方法。(类列表如下图)
基本的方法是GetCalculation类里面的Get_Calculation()方法,这个方法生成了最基本的算术表达式。GetSymbol类是main()所在的类,用来启动函数。
主要类之间具体的调用过程:
5、代码说明:
因为代码的量比较大,所以只是展示一部分关键代码,完整的代码可以去GitHub自行查看。
思路:这只是生成算数表达式的代码的一部分,整个的算术表达式分成了很多种情况,要解决的问题就是将随机得到的数字和运算符拼接成一个完整的、格式统一的算术表达式。其实这个只需要字符串的拼接就行了,真正的问题是如何保证得到的表达式的结果不为负数。这里使用的方法就是利用运算符的下标位置进行判断,因为只有减号才会出现负数,所以在减号出现的表达式里都会对减号之前的数值和减号之后的数值进行比较,如果前者小于后者,那么舍弃这个算式,重新生成新的算式。
public static String Get_Calculation1(int m) {
String strings=String.valueOf(Getnum.Get_num(m))+" "+GetSymbol.Get_Symbol()+" "+String.valueOf(Getnum.Get_num(m))+" "+"=";
String answer=null;
String string =strings.replaceAll(" ", "");
String[] news=string.split("\\-|\\=|\\+|\\×|\\÷");
int a=Integer.parseInt(news[0]);
int b=Integer.parseInt(news[1]);
if(string.contains("-")) {
if(a<b) {
return Get_Calculation1(m);
}
else {
answer=String.valueOf(GetResult.Num_sub_Num(a, b));
return strings+"&"+answer;
}
}
if (string.contains("+")) {
answer=String.valueOf(GetResult.Num_add_Num(a, b));
return strings+"&"+answer;
}
if (string.contains("×")) {
answer=String.valueOf(GetResult.Num_mul_Num(a, b));
return strings+"&"+answer;
}else {
answer=GetResult.Num_div_Num(a, b);
return strings+"&"+answer;
}
}
public static String Get_Calculation2(int m) {
String strings=String.valueOf(Getnum.Get_num(m))+" "+GetSymbol.Get_Symbol()+" "+Getnum.Get_Frac(m)+" "+"=";
String string =strings.replaceAll(" ", "");
String[] string1=string.split("\\+|\\-|\\×|\\÷|\\=");
String answer=null;
int n=Integer.parseInt(string1[0]);
String string2=string1[1];
if(string.contains("-")) {
answer=GetResult.Num_sub_Fra(n, string2);
if (answer.contains("-")) {
return Get_Calculation2(m);
}
else if (!answer.contains("-")) {
return strings+"&"+answer;
}
}
if(string.contains("+")) {
answer=GetResult.Num_add_Fra(n, string2);
return strings+"&"+answer;
}
if (string.contains("×")) {
answer=GetResult.Num_mul_Fra(n, string2);
return strings+"&"+answer;
}
else {
answer=GetResult.Num_div_Fra(n, string2);
return strings+"&"+answer;
}
}
检查答案正误的方法:
思路:因为之前得到算数表达式的时候对格式严格要求了,所以在判断正误的时候就简单了很多,题目文件中的内容和答案文件中的内容就只是差了答案这一部分,所以在判断正误的时候readline每一行,并且进行比较,把equals的结果记录下来就是最终需要统计的数据。
package Com.software;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;
public class Check {
WriteStringToFile ws = new WriteStringToFile();
void checkAnswer(File exercises,File answers) throws IOException{
BufferedReader br_e = new BufferedReader(new FileReader(exercises));
BufferedReader br_a = new BufferedReader(new FileReader(answers));
String line_e = null;
String line_a = null;
File fileGrade = new File("Grade.txt");
int numTrue = 0;
int numFalse = 0;
if(fileGrade.exists())fileGrade.delete();
while((line_e = br_e.readLine()) != null && (line_a = br_a.readLine()) != null){
if(!line_e.equals(line_a)){
ws.writeToFile("Grade.txt", line_e);
ws.writeToFile("Grade.txt", "\n");
// System.out.println(line_e);
numFalse++;
}else{
numTrue++;
}
}
br_e.close();
br_a.close();
String resultCheck = "一共做错"+numFalse+"道,做对"+numTrue+"道。";
// System.out.println("共有"+num+"道错题");
ws.writeToFile("Grade.txt", resultCheck);
}
}
写入文件的IO 方法:
思路:为了保证程序多次运行时生成文件里的内容会一直刷新,所以每次程序运行都会删掉之前的文件,重新建立一个新的文件来存储题目和答案。
package Com.software;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteStringToFile {
public void writeToFile(String filePath,String string){
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filePath,true);
} catch (FileNotFoundException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//true表示在文件末尾追加
try {
fos.write(string.getBytes());
// fos.write("\n".getBytes());
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
try {
fos.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
6、测试运行:
控制台输入:
生成文件:
代码覆盖率:
7、PSP表格:
Planning | 计划 | 10 | 5 |
· Estimate | · 估计这个任务需要多少时间 | 800 | 1200 |
Development | 开发 | 480 | 630 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 30 |
· Design Spec | · 生成设计文档 | 60 | 80 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 45 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 30 | 60 |
· Coding | · 具体编码 | 120 | 360 |
· Code Review | · 代码复审 | 30 | 45 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 120 | 120 |
· Test Report | · 测试报告 | 60 | 30 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1200 | 1610 |
8、项目小结:
这个项目最大的一个特点不是项目本身,而是项目的开发模式,结对编程是之前没有接触过的一种编程方式,也是以后工作可能会经常使用的一种开发模式。这次的项目与乐滔同学一起完成,分工方面乐滔同学完成了项目的四则表达式的生成部分(其中包含了对各种运算符的判断问题以及括号的添加位置和结果的非负性保证)和答案的生成部分,我主要完成的是文件的IO读写操作(之前乐滔同学在写的时候使用的控制台输出),以及最后的答案判断问题。代码数量方面乐滔同学编写的代码要远远大于我编写的代码数量。问题探讨方面只要体现在对于四则表达式生成方法以及答案判断正误的部分,双方各自表达了自己的观点,每个人负责自己的部分,遇到的困难解决起来也就方便了很多。