结对项目:自动生成小学四则运算题目
| 所属课程 | 软件工程导论 |
|---|---|
| 作业要求 | 结对项目 |
| 作业目标 | 团队简介、分工合作、确定选题、团队计划安排 |
小组成员
| 姓名 | 班级 | 学号 |
|---|---|---|
| 林劲辰 | 计科2班 | 3121004707 |
| 许庆阳 | 计科2班 | 3121004931 |
Github链接: https://github.com/HsuQingYoung/PeerWork
项目要求
1、实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)
2、程序应能支持一万道题目的生成。
3、使用 n 参数控制生成题目的个数,使用 r 参数控制题目中数值(自然数、真分数和真分数分母)的范围
4、不能出现负数,假分数,每题运算符不能超过3个
5、生成的题目存入执行程序的当前目录下的Exercises.txt文件,在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件
6、程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,统计结果输出到文件Grade.txt
1.PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 30 |
| Estimate | 估计这个任务需要多少时间 | 10 | 10 |
| Development | 开发 | 200 | 250 |
| Analysis | 需求分析 (包括学习新技术) | 80 | 80 |
| Design Spec | 生成设计文档 | 40 | 50 |
| Design Review | 设计复审 | 20 | 30 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
| Design | 具体设计 | 60 | 60 |
| Coding | 具体编码 | 150 | 180 |
| Code Review | 代码复审 | 20 | 30 |
| Test | 测试(自我测试,修改代码,提交修改) | 90 | 110 |
| Reporting | 报告 | 80 | 50 |
| Test Repor | 测试报告 | 20 | 40 |
| Size Measurement | 计算工作量 | 20 | 20 |
| Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
| 合计 | 900 | 1000 |
2.模块接口设计
2.1流程图

2.2框架

2.3类
| 类名 | 作用 |
|---|---|
| main | 主函数 |
| BuildFormula | 随机生成式子 |
| checkAnswer | 对照答案 |
| FileOperation | 文件相关 |
| Fraction | 表示分数 |
| GetResultFromArray | 转换分数 |
2.4函数
| 函数名 | 功能 | 归属的类 |
|---|---|---|
| generate | 随机生成公式,如果有减法,则判定减数和被减数的大小,确保计算的子过程不出现负数 | BuildFormula类 |
| addBrackets | 符合条件的话随机生成括号 | BuildFormula类 |
| check | 讲用户输入的答案与正确答案比较,正确则在后面打勾,否则打叉 | checkAnswer类 |
| writeFormulaIntoTxt | 讲生成的公式写入文件 | FileOperation类 |
| writeAnswerIntoTxt | 讲每条公式对应的答案写入文件 | FileOperation类 |
| createFraction | 根据分子分母构建分数 | Fraction类 |
| add | 分数的加操作 | Fraction类 |
| sub | 分数的减操作 | Fraction类 |
| mul | 分数的乘操作 | Fraction类 |
| div | 分数的除法操作 | Fraction类 |
| toString | 分数转换为字符串 | Fraction类 |
| calculateStringArray | 根据字符串数组中的公式将其转换为分数并计算其结果 | GetResultFromArray类 |
函数之间的关系:BuildFormula类和GetResultFromArray类的函数分别根据所给的题目生成要求来生成题目和对应的答案,Fraction类的函数是为GetResultFromArray类的函数生产答案的过程提供服务,而checkAnswer类的函数则是为了对照答案是否正确。
2.5关键代码以及注释
式子生成:
点击查看代码
public void generate(){//生成一条公式1 + 1 + 1
Random random=new Random();
StringBuilder sb=new StringBuilder();
Fraction result=null;
String[] formula=new String[count];
String[] answer=new String[count];
for(int j=0;j<count;j++){
int numOfFraction=random.nextInt(3)+2;
boolean bracketsGenerate=random.nextBoolean();//是否随机生成括号
sb.setLength(0);
result=null;
int tag=0;
int operator;
for(int i=1;i<=numOfFraction;i++){//根据分数的个数生成公式和符号
if(i!=numOfFraction){
int denominator=random.nextInt(10)+1;//分母
int numerator=random.nextInt(denominator*range)+1;//分子
Fraction f=new Fraction(numerator,denominator);
sb.append(f.toString()+" ");
if(tag==0){
operator=random.nextInt(4);
}else{
operator=random.nextInt(2)+2;
}
if(operator==0||operator==1){
tag=1;
}
switch (operator) {
case 0:
sb.append("÷ ");
break;
case 1:
sb.append("- ");
break;
case 2:
sb.append("* ");
break;
case 3:
sb.append("+ ");
break;
}
}else{
int denominator=random.nextInt(10)+1;//分母
int numerator=random.nextInt(denominator*range)+1;//分子
Fraction f=new Fraction(numerator,denominator);
sb.append(f.toString());
}
}
String[] s=sb.toString().split(" ");
s=swapOrder(s);
if(numOfFraction!=2&&bracketsGenerate){//符合生成括号的条件
//String[] s=sb.toString().split(" ");
int leftindex=2*(random.nextInt(numOfFraction-1));//4个操作时 0 1 2
int rightindex=0;
if(numOfFraction==3){
switch (leftindex){
case 0:
rightindex=2;
break;
case 2:
rightindex=4;
break;
}
}else if(numOfFraction==4){
switch(leftindex){
case 0:
rightindex=2*(random.nextInt(2)+1);
break;
case 2:
rightindex=2*(random.nextInt(2)+2);
break;
case 4:
rightindex=6;
break;
}
}
GetResultFromArray g=new GetResultFromArray();
String[] news=null;
news=addBrackets(s,leftindex,rightindex);
result=g.calculateStringArray(news);
sb.setLength(0);
for (String str : news) {
sb.append(str+" ");
}
}else{
GetResultFromArray g=new GetResultFromArray();
//s=sb.toString().split(" ");
result=g.calculateStringArray(s);
}
sb.append("= ");
formula[j]=sb.toString();
if(result==null){
answer[j]="不可计算";
}else{
answer[j]=result.toString();
}
}
FileOperation fo=new FileOperation();
fo.writeFormulaIntoTxt(formula);
fo.writeAnswerIntoTxt(answer);
}
计算答案:
点击查看代码
public static Fraction calculateStringArray(String[] inputArray) {
if (inputArray == null || inputArray.length == 0) {
return new Fraction(0, 1); // 返回默认分数 0/1
}
Stack<Fraction> numStack = new Stack<>();
Stack<Character> operatorStack = new Stack<>();
for (String token : inputArray) {
if (isFraction(token)) {
numStack.push(new Fraction(token));
} else if (token.equals("(")) {
operatorStack.push('(');
} else if (token.equals(")")) {
while (!operatorStack.isEmpty() && operatorStack.peek() != '(') {
if (performOperation(numStack, operatorStack)) {
return null;
}
}
operatorStack.pop(); // Pop the opening parenthesis
} else if (isOperator(token)) {
while (!operatorStack.isEmpty() && hasPrecedence(token.charAt(0), operatorStack.peek())) {
if (performOperation(numStack, operatorStack)) {
return null;
}
}
operatorStack.push(token.charAt(0));
}
}
while (!operatorStack.isEmpty()) {
if (performOperation(numStack, operatorStack)) {
return null;
}
}
return numStack.pop();
}
答案对照:
点击查看代码
public void check() throws IOException {
String filePath="D:\\IdeaProjects\\PeerWork\\src\\main\\resources\\Exercises.txt";
String filePath1="D:\\IdeaProjects\\PeerWork\\src\\main\\resources\\Answer.txt";
String[] useranswer=readLinesToArray(filePath);
String[] realanswer=readLinesToArray(filePath1);
String[] result=new String[useranswer.length];
int i=0;
for(String line:useranswer){
String[] ss=line.split(" ");
String answer=ss[ss.length-1];//用户写的
int index=realanswer[i].indexOf("、");
String realAnswer=realanswer[i].substring(index+1);
if(realAnswer.equals(answer)){
result[i]="✓";
}else{
result[i]="×";
}
i++;
}
List<String> lines = new ArrayList<>();
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String line;
i=0;
while ((line = reader.readLine()) != null) {
lines.add(line + result[i]);
i++;
}
reader.close();
BufferedWriter writer = new BufferedWriter(new FileWriter(filePath));
for (String updatedLine : lines) {
writer.write(updatedLine);
writer.newLine();
}
writer.close();
}
3代码性能分析


改善过程:首先,我们发现在式子生成的过程中由于查重、判断是否成立的代码部分存在冗余,所以我们优化了代码,使BuildFormula类的函数更加简洁,提高generate函数的运行效率。然后观察性能分析图后,我们发现GetResultFromArray类在程序运行过程中消耗较大,于是我们仔细查看了代码,减去了代码中不必要的循环,一定程度上减少了程序运行的消耗。
4单元测试展示
4.1测试代码
生成测试
点击查看代码
public class TestMain {
public static void main(String[] args) {//测试生成公式和答案
//-n 10 -r 10
int range=10;
int count=10;
BuildFormula bf=new BuildFormula(range,count);
bf.generate();
}
}
答案测试
点击查看代码
public class TestCheckAnswer {//测试校对答案
public static void main(String[] args) {
checkAnswer ca=new checkAnswer();
try {
ca.check();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.2测试结果
式子生成:

答案生成:

命令行测试

测试结果:成功生成式子,并且结果正确,没有出现问题。
4.3测试覆盖率

浙公网安备 33010602011771号