结对项目-四则运算生成器
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/Networkengineering1834 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/Networkengineering1834/homework/11148 |
这个作业的目标 | < 与一个队友结对实现一个自动生成小学四则运算题目的命令行程序 > |
结对成员:
- 张朝霖:3118005346
- 杨金华:3118005342
目录
目录
1、Github仓库
https://github.com/ChaplinLittleJenius/SecondProjectZCL
2、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟 ) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 100 | 120 |
· Estimate | · 估计这个任务需要多少时间 | 100 | 120 |
Development | 开发 | 460 | 550 |
· Analysis | · 需求分析 (包括学习新技术) | 120 | 120 |
· Design Spec | · 生成设计文档 | 20 | 20 |
· Design Review | · 设计复审 | 30 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
· Design | · 具体设计 | 60 | 80 |
· Coding | · 具体编码 | 180 | 200 |
· Test | · 测试(自我测试,修改代码,提交修改) | 30 | 40 |
Reporting | 报告 | 120 | 110 |
· Test Repor | · 测试报告 | 80 | 60 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 40 |
· 合计 | 680 | 780 |
3、程序设计与实现过程
计算器模块
-
CalculateExpression类
- calculateExp方法:计算逆波兰表达式的子式
- calculateRPN方法:计算逆波兰表达式
- getExpressionAnswer方法:获取逆波兰表达式并计算,得到答案
-
CalculateUtil类
- getLength方法:获取字符串数组的长度
- getRPN方法:将表达式转成逆波兰表达式的字符串数组形式
- getFractionInt方法:获取带分数的整数部分
- getDenominator方法:获取分数的分母
- getNumerator方法:获取分数的分子
- getLCM方法:获取最小公倍数
- add方法:两个分数相加
- sub方法:两个分数相减
- multiply方法:两个分数相乘
- divide方法:两个分数相除
- calculate方法:根据flag值对分数进行不同的计算
- convertAnswerFormat方法:将假分数转换成带分数
- getImproperFraction方法:获取假分数
表达式模块
-
Expression类
- getExpression方法:获取字符串表达式
- getStringArrayExpression方法:获取字符串数组形式的表达式
随机生成模块
-
RandElement类
- getOperatorNum方法:随机生成操作数
- getIntegerOperatorNum方法:随机生成整数类型的操作数
- getRandomNum方法:生成随机数
- getOperatorChar方法:随机生成运算符
- getProperFraction方法:随机生成真分数
- getImproperFraction方法:随机生成假分数
- getCorrectOperatorNum方法:获取合法的操作数
-
RandExpression类
- getExpression方法:随机生成表达式
- getOneOperatorExpression方法:生成单操作符表达式
- getOneOperatorExpressionWithBrackets方法:生成一个带括号的单操作符表达式
- getTwoOperatorExpression方法:生成带两个操作符表达式
- getTwoOperatorExpressionWithLeftBrackets方法:生成带左括号的两个操作符表达式
- getTwoOperatorExpressionWithRightBrackets方法:生成带右括号的两个操作符表达式
- getTwoOperatorExpressionWithMidBrackets方法:生成带大括号两个操作符表达式
- getThreeOperatorExpression方法:生成带三个操作符表达式
- getThreeOperatorExpressionWithLeftBrackets方法:生成带左括号的三个操作符的表达式
- getThreeOperatorExpressionWithRightBrackets方法:生成带右括号的三个操作符表达式
测试文件
-
Main类:程序的入口
-
MyTest类:单元测试类
-
Util类
- doMain方法:将main函数整合成一个静态方法,方便代码复用
- checkAnswer方法:判断答案对错
- printList方法:输出函数
总类图
关键代码
import calculator.CalculateExpression;
import model.Expression;
import random.RandExpression;
import java.io.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Util {
/**
* main函数整合成一个静态方法,方便代码复用
*
* @param expressionNum 表达式个数
* @param operatorNumRange 操作数范围
*/
public static void doMain(int expressionNum, int operatorNumRange, String exercisesPath, String answersPath) throws IOException {
RandExpression randExpression = new RandExpression();
CalculateExpression calculateExpression = new CalculateExpression();
// 习题数组
String[] exercises = new String[expressionNum];
// 答案数组
String[] answers = new String[expressionNum];
// 习题集合,用于判重
Set<String> exercisesSet = new HashSet<String>();
// 文件路径
File exercisesFile = new File(exercisesPath);
File answersFile = new File(answersPath);
if (!exercisesFile.exists() || !answersFile.exists()) {
throw new IOException("找不到指定的文件!");
}
BufferedWriter exercisesWriter = new BufferedWriter(new FileWriter(exercisesFile, true));
BufferedWriter answerWriter = new BufferedWriter(new FileWriter(answersFile, true));
// 生成指定数目的表达式
for (int i = 0; i < expressionNum; i++) {
Expression expression = randExpression.getExpression(operatorNumRange);
calculateExpression.getExpressionAnswer(expression);
if (expression.getAnswer() == null) {
i--;
} else {
String exp = expression.getExpression();
if (exercisesSet.contains(exp)) {
i--;
} else {
exercisesSet.add(exp);
exercises[i] = exp;
answers[i] = expression.getAnswer();
}
}
}
// 输出结果
for (int i = 0; i < expressionNum; i++) {
System.out.print("第" + (i + 1) + "题:");
System.out.println(exercises[i] + " = " + answers[i]);
exercisesWriter.write((i + 1) + "." + exercises[i] + "=\n");
answerWriter.write((i + 1) + "." + answers[i] + "\n");
}
// 刷新并关闭IO流
exercisesWriter.flush();
answerWriter.flush();
exercisesWriter.close();
answerWriter.close();
}
/**
* 判断答案对错
*
* @param exercisesPath 用户答案路径
* @param answersPath 正确答案路径
* @param gradePath 成绩路径
* @throws IOException IO异常
*/
public static void checkAnswer(String exercisesPath, String answersPath, String gradePath) throws IOException {
File exercisesFile = new File(exercisesPath);
File answersFile = new File(answersPath);
File gradeFile = new File(gradePath);
if (!exercisesFile.exists() || !answersFile.exists() || !gradeFile.exists()) {
throw new IOException("找不到指定的文件!");
}
List<String> correctList = new ArrayList<>();
List<String> wrongList = new ArrayList<>();
BufferedReader exercisesReader = new BufferedReader(new FileReader(exercisesFile));
BufferedReader answerReader = new BufferedReader(new FileReader(answersFile));
BufferedWriter gradeWriter = new BufferedWriter(new FileWriter(gradeFile, true));
String exercisesAnswer, answer;
int count = 0;
while ((answer = answerReader.readLine()) != null) {
if ((exercisesAnswer = exercisesReader.readLine()) != null) {
count++;
String[] str1 = exercisesAnswer.split("=");
if(str1.length <= 1){
wrongList.add(count + "");
continue;
}
String[] str2 = answer.split("\\.");
if ((str1[1].trim()).equals(str2[1].trim())) {
correctList.add(str2[0]);
} else {
wrongList.add(str2[0]);
}
} else {
count++;
wrongList.add(count + "");
}
}
StringBuilder stringBuilder = new StringBuilder("Correct:");
stringBuilder.append(correctList.size()).append("(");
printList(stringBuilder, correctList);
stringBuilder.append(")\n");
stringBuilder.append("Wrong:").append(wrongList.size()).append("(");
printList(stringBuilder, wrongList);
stringBuilder.append(")\n");
gradeWriter.write(stringBuilder.toString());
System.out.println(stringBuilder.toString());
// 刷新并关闭IO流
exercisesReader.close();
answerReader.close();
gradeWriter.flush();
gradeWriter.close();
}
public static void printList(StringBuilder stringBuilder, List<String> list) {
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
stringBuilder.append(list.get(i));
} else {
stringBuilder.append(list.get(i)).append(",");
}
}
}
}
程序流程
4、性能测试
-
CPU占用频率
-
内存空间占用统计
5、部分单元测试展示
- testGenerateExpression测试:测试能否生成表达式,测试结果为程序可以正确生成表达式。
- testCheckAnswer测试:测试能否对计算结果进行检错,即针对"需求9"的设计。本次直接利用程序生成的100道题进行测试,在正确回答前5道题后对文件进行测试,测试结果为程序可以对计算结果进行检错。
6、程序测试展示
-
第一次
-
第二次
-
第三次
-
第四次
-
第五次
-
第六次
-
第七次
-
第八次
-
第九次
-
第十次
7、项目总结
-
张朝霖
这次结对编程算是我第一次参与团队项目开发,在整个合作开发过程中遇到了不少问题,但是好在最后大部分都解决了。
开发初期大多时间都耗费在了团队的分工上,包括代码的编写,四则运算底层的实现逻辑等都需要经常和队友交流。四则
运算实现的过程中我遇到的最大的困难就是如何随机生成一个符合题意的表达式,尤其是涉及到分数的处理时,原本有两
种思路,第一种是用一个实体类Fraction封装,第二种则是直接当成String,在计算的时候另作处理。最后我们还是选
择了第二种,所以整体代码感觉比预期冗余了很多。虽然整个代码还有很多待完善的功能,不过对于第一次合作项目来说,
个人还是挺满意的了。
-
杨金华
第一次参与团队项目开发,最初计划的时候我们花了不少时间去讨论。在算法编写的过程中,我们也遇到了许多问题,经
过我们不断的查阅和探讨,问题才逐个被攻破。但是在处理分数的选择上,我们纠结了许久,最终还是选择了String的方
式去处理。此外,我由于个人能力欠缺的问题,我更多的是负责了代码的理解、复审和程序的测试,我们能完成整个项目
的开发,更多的功劳在于队友的努力和指导。经过多天的结对学习,我已意识到自己的不足之处,这给了我一定的压力和
无限的动力去提升自己。