结对作业
| 这个作业属于哪个课程 | 网工19-软件工程 |
|---|---|
| 这个作业的要求 | 作业要求 |
| 这个作业的目标 | 实现一个自动生成小学四则运算题目的命令行程序 |
| 小队成员 | 3119005340孙斌 3119005323黄宇洋 |
| Github地址 | Github链接 |
一、PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 15 | 10 |
| · Estimate | · 估计这个任务需要多少时间 | 15 | 10 |
| Development | 开发 | 1055 | 1220 |
| · Analysis | · 需求分析 (包括学习新技术) | 200 | 270 |
| · Design Spec | · 生成设计文档 | 60 | 70 |
| · Design Review | · 设计复审 (和同事审核设计文档) | 30 | 20 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 25 | 15 |
| · Design | · 具体设计 | 200 | 290 |
| · Coding | · 具体编码 | 400 | 440 |
| · Code Review | · 代码复审 | 70 | 60 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 70 | 55 |
| Reporting | 报告 | 85 | 65 |
| · Test Report | · 测试报告 | 35 | 30 |
| · Size Measurement | · 计算工作量 | 20 | 15 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 20 |
| 合计 | 1155 | 1295 |
二、模块设计
模块结构

FileWrite类:写文件,将生成的题目和答案写入文件Execrises.txt和Answer.txt中
FormulaCheck类:检查生成的式子是否重复
FormulaCreate类:生成随机的四则运算式子
ResultsCalculate类:计算生成的题目的答案
三、部分代码说明
FileWrite类
//生成并输出Exercises.txt、Answer.txt
public void createProblemSet(int n,int r){
FormulaCheck temporarySet = new FormulaCheck();
ArrayList returnList = temporarySet.generate(n,r);
ArrayList<String> exercisesList = new ArrayList<>();
ArrayList<String> answerList = new ArrayList<>();
for (int i = 0;i < 2*n;i++) {
if(i < n) exercisesList.add(returnList.get(i).toString());
else answerList.add(returnList.get(i).toString());
}
createExercisesFile(exercisesList);
createAnserFile(answerList);
}
//生成并输出Exercises.txt
public static void createExercisesFile(ArrayList txtList){
try{
File exercisesTxt = new File("Exercises.txt");
//若文件存在,删除文件
if (exercisesTxt.exists()) {
exercisesTxt.delete();
}
if(exercisesTxt.createNewFile()){
System.out.println("\n————生成Exercises.txt中————");
FileOutputStream txtFile = new FileOutputStream(exercisesTxt);
PrintStream q = new PrintStream(exercisesTxt);
for(int i = 0;i < txtList.size();i++){
q.println(i+1 + ". " +txtList.get(i));
}
txtFile.close();
q.close();
System.out.println("\n****** Exercises.txt 生成成功! ******");
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}
//生成并输出Answer.txt
public static void createAnserFile(ArrayList ansList){
try{
File answerTxt = new File("Answer.txt");
//若文件存在,删除文件
if (answerTxt.exists()) {
answerTxt.delete();
}
if(answerTxt.createNewFile()){
System.out.println("\n————生成Answer.txt中————");
FileOutputStream ansFile = new FileOutputStream(answerTxt);
PrintStream p = new PrintStream(answerTxt);
for(int i = 0;i < ansList.size();i++){
p.println(i+1 + ". " +ansList.get(i));
}
ansFile.close();
p.close();
System.out.println("\n****** Answer.txt 生成成功! ******");
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}
// 生成并输出Grade.txt
public void createGradeFile(String submitPath, String answersPath) {
try {
ArrayList<String> submitList = obtainAnswer(submitPath);
ArrayList<String> answersList = obtainAnswer(answersPath);
ArrayList<String> WQuesNum = new ArrayList<>();
ArrayList<String> TQuesNum = new ArrayList<>();
for (int i = 0; i < submitList.size(); i++) {
if (submitList.get(i).equals(answersList.get(i)))
WQuesNum.add(String.valueOf(i+1));
else
TQuesNum.add(String.valueOf(i+1));
}
File gradeTxt = new File("Grade.txt");
//若文件存在,删除文件
if (gradeTxt.exists()) {
gradeTxt.delete();
}
if (gradeTxt.createNewFile()) {
System.out.print("创建Grade.txt:");
FileOutputStream gradeFile = new FileOutputStream(gradeTxt);
PrintStream p = new PrintStream(gradeTxt);
p.print("Correct:");
output(p, TQuesNum);
p.print("Wrong:");
output(p, WQuesNum);
gradeFile.close();
p.close();
System.out.println("Grade.txt 创建成功!");
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}
//输出答案
private void output(PrintStream p,ArrayList quesNum) {
p.print(quesNum.size() +"(");
for(int i = 0;i < quesNum.size();i++){
System.out.print(">");
if (i < quesNum.size()-1)
p.print(" " + quesNum.get(i) + ",");
else
p.print(" " + quesNum.get(i));
}
p.println(" )\n");
}
//获取文件答案
private ArrayList<String> obtainAnswer(String path) throws IOException {
ArrayList<String> answerList = new ArrayList<>();
BufferedReader answerFile = new BufferedReader(new FileReader(path));
String answerLine = null;
while ((answerLine = answerFile.readLine()) != null) {
answerLine = answerLine.replace(" ", "");
if (answerLine.indexOf('.') >= 0) {
if (answerLine.length() > 2)
answerList.add(answerLine);
}
}
return answerList;
}
ResultsCalculate类
public String[] checkout(String formula, int length) {
Stack<String> stackN = new Stack<>();//操作数
Stack<String> stackO = new Stack<>();//操作符
String[] RPN = new String[length];//逆波兰表达式
// 哈希表存放运算符优先级
HashMap<String, Integer> hashmap = new HashMap<>();
hashmap.put("(", 0);
hashmap.put("+", 1);
hashmap.put("-", 1);
hashmap.put("×", 2);
hashmap.put("÷", 2);
for (int i = 0, j = 0; i < formula.length(); ) {
StringBuilder num = new StringBuilder();
//切割式子
char c = formula.charAt(i);
//若c为数字,存入num
while (Character.isDigit(c) || c == '/' || c == '\'') {
num.append(c);
i++;
c = formula.charAt(i);
}
//num里无数字,处理符号
if (num.length() == 0) {
switch (c) {
//如果是“(”转化为字符串压入字符栈
case '(': {
stackO.push(String.valueOf(c));
break;
}
//遇到“)”了,进行计算
case ')': {
String operator = stackO.pop();
//符号栈里有符号时,取操作数运算
while (!stackO.isEmpty() && !operator.equals("(")) {
String a = stackN.pop();
String b = stackN.pop();
//后缀表达式变形
RPN[j++] = a;
RPN[j++] = b;
RPN[j++] = operator;
String ansString = calculate(b, a, operator);
if (ansString == null)
return null;
//将结果压入栈
stackN.push(ansString);
//符号指向下一个计算符号
operator = stackO.pop();
}
break;
}
//遇到了“=”,计算最终结果
case '=': {
String operator;
while (!stackO.isEmpty()) {
operator = stackO.pop();
String a = stackN.pop();
String b = stackN.pop();
//后缀表达式变形
RPN[j++] = a;
RPN[j++] = b;
RPN[j++] = operator;
String ansString = calculate(b, a, operator);
if (ansString == null)
return null;
stackN.push(ansString);
}
break;
}
//其他
default: {
String operator;
while (!stackO.isEmpty()) {
operator = stackO.pop();
if (hashmap.get(operator) >= hashmap.get(String.valueOf(c))) { //比较优先级
String a = stackN.pop();
String b = stackN.pop();
//后缀表达式变形
RPN[j++] = a;
RPN[j++] = b;
RPN[j++] = operator;
String ansString = calculate(b, a, operator);
if (ansString == null)
return null;
stackN.push(ansString);
} else {
stackO.push(operator);
break;
}
}
stackO.push(String.valueOf(c)); //将符号压入符号栈
break;
}
}
}
//处理数字,直接压栈
else {
stackN.push(num.toString());
continue;
}
i++;
}
//栈顶数字为答案
RPN[length - 3] = "=";
RPN[length - 2] = stackN.peek();
RPN[length - 1] = formula;
return RPN;
}
//计算式子
private String calculate(String m, String n, String operator) {
String ansFormula = null;
char op = operator.charAt(0);
int[] indexFraction = {m.indexOf('\''), m.indexOf('/'), n.indexOf('\''), n.indexOf('/')};//分数 各部分 切割位置
//处理分数运算
if (indexFraction[1] > 0 || indexFraction[3] > 0) {
int[] denominator = new int[3];
int[] molecule = new int[3];
int[] integralPart = new int[3];
//切割分数
if (indexFraction[1] > 0) {
for (int i = 0; i < m.length(); i++) {
if (i < indexFraction[0]) {
integralPart[0] = Integer.parseInt(integralPart[0] + String.valueOf(m.charAt(i) - '0'));
} else if (i > indexFraction[0] && i < indexFraction[1]) {
molecule[0] = Integer.parseInt(molecule[0] + String.valueOf(m.charAt(i) - '0'));
} else if (i > indexFraction[1]) {
denominator[0] = Integer.parseInt(denominator[0] + String.valueOf(m.charAt(i) - '0'));
}
}
} else {
integralPart[0] = Integer.parseInt(m);
denominator[0] = 1;
molecule[0] = 0;
}
if (indexFraction[3] > 0) {
for (int i = 0; i < n.length(); i++) {
if (i < indexFraction[2]) {
integralPart[1] = Integer.parseInt(integralPart[1] + String.valueOf(n.charAt(i) - '0'));
} else if (i > indexFraction[2] && i < indexFraction[3]) {
molecule[1] = Integer.parseInt(molecule[1] + String.valueOf(n.charAt(i) - '0'));
} else if (i > indexFraction[3]) {
denominator[1] = denominator[1] + n.charAt(i) - '0';
}
}
} else {
integralPart[1] = Integer.parseInt(n);
denominator[1] = 1;
molecule[1] = 0;
}
//分数运算
switch (op) {
case '+': {
denominator[2] = denominator[0] * denominator[1];
molecule[2] = integralPart[0] * denominator[2] + molecule[0] * denominator[1] + integralPart[1] * denominator[2] + molecule[1] * denominator[0];
break;
}
case '-': {
denominator[2] = denominator[0] * denominator[1];
molecule[2] = integralPart[0] * denominator[2] + molecule[0] * denominator[1] - integralPart[1] * denominator[2] - molecule[1] * denominator[0];
break;
}
default:
return null;
}
//提取整数部分
if (molecule[2] >= denominator[2] && molecule[2] > 0) {
integralPart[2] = molecule[2] / denominator[2];
molecule[2] = Math.abs(molecule[2] % denominator[2]);
} else if (molecule[2] < 0) {
return null;
}
//化简分数
if (molecule[2] != 0) {
ansFormula = greatFraction(integralPart[2], molecule[2], denominator[2]);
} else ansFormula = String.valueOf(integralPart[2]);
} else { //处理整数运算
int a = Integer.parseInt(m);
int b = Integer.parseInt(n);
switch (op) {
case '+': {
ansFormula = String.valueOf(a + b);
break;
}
case '-': {
if (a - b >= 0)
ansFormula = String.valueOf(a - b);
else
return null;
break;
}
case '×': {
ansFormula = String.valueOf(a * b);
break;
}
case '÷': {
if (b == 0) {
return null;
} else if (a % b != 0) {
ansFormula = a % b + "/" + b;
if (a / b > 0) ansFormula = a / b + "'" + ansFormula;
} else
ansFormula = String.valueOf(a / b);
break;
}
}
}
return ansFormula;
}
//化简分数
private String greatFraction(int integralPart, int molecule, int denominator) {
String ansFormula;
int commonFactor = 1;
//求最大公约数
FormulaCreate create = new FormulaCreate();
commonFactor = create.commonFactor(denominator, molecule);
//化简分数
denominator /= commonFactor;
molecule /= commonFactor;
//带分数
if (integralPart == 0 && molecule > 0) {
ansFormula = String.valueOf(molecule) + '/' + String.valueOf(denominator);
} else if (molecule == 0)
ansFormula = String.valueOf(integralPart);
else {
ansFormula = String.valueOf(integralPart) + "'" + String.valueOf(molecule) + '/' + String.valueOf(denominator);
}
return ansFormula;
}
四、性能分析


五、测试实例
10道题目
输入指令

生成的Execrises.txt文件

生成的Answer.txt文件

10000道题目
指令输入

生成的Execrises.txt文件

生成的Answer.txt文件

六、项目小结
黄宇洋:这是我第一次进行多人共同编程,没有太多的协同合作的经验,再加上我们两个人的水平很低很低,所以这一次的编程进行的磕磕绊绊,从进行分工到各自完成完成任务经历了很多的困难,也从网上找了许多资料参考。通过了这次的结对作业,充分认识到了编程能力的不足,以及了解到了协同合作的重要性和艰难,也更多的了解了软件工程这门课程。
孙斌:本次结对项目对于我们而言,其意义是更多地去考验我们的分工协调和如何高效合作。通过这次结对项目,我们明白了好搭档一起合作对于项目的进程有很大的帮助外,也让我们更加有责任心,知道项目不只是一个人的事情。

浙公网安备 33010602011771号