结对编程:小学四则运算
这个作业属于哪个课程 | 软件工程课程 |
---|---|
这个作业要求在哪里 | 个人项目 - 作业 - 计科22级34班 - 班级博客 - 博客园 (cnblogs.com) |
这个作业的目标 | |
成员一 | 迪力拜尔3222004889 |
成员二 | 坤杜孜阿依3222004768 |
github链接 https://github.com/dilibar-code/pair-item3222004889-4768 |
目录
- 1.需求分析
- 1.1题目
- 1.2需求分析
- 2.开发环境
- 2.1工程结构
- 2.2总体设计
- 3.代码实现及设计
- 3.1 Main类
- 3.2 Create类
- 3.3 CheckRepeat类
- 3.4 CheckAnswer 类
- 3.5 problemSet类
- 4.测试
- 4.1性能分析和内存分析
- 4.2测试运行
- 5.项目小结
- 6.PSP表格
1.需求分析
1.1题目
1.实现一个自动生成小学四则运算题目的命令行程序(也可以用图像界面,具有相似功能)。
2.生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
3.生成的题目中如果存在形如e1÷ e2的子表达式,那么其结果应是真分数。
4.每道题目中出现的运算符个数不超过3个。程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
1.2需求分析
1.读取命令行参数,随机生成符合参数的题目。
2.对输出的式子进行查重、计算。
3.生成并输出题集文本、答案文本。
4.命令行输入答案文本、答题文本路径,读取路径后与正确答案进行比对。
5.输出成绩文本,包括正确题号与错误题号。
2.开发环境
- 编程语言:Java
- IDE:eclipse
- 性能分析工具:JProfile
2.1工程结构
2.2总体设计
3.代码实现及设计
3.1 Main类
获取命令行参数,调用相应函数。
3.2 Create类
随机生成满足要求的题目。
createFormula方法用于生成整数、分数、运算符并将其合成式子,最后调用CheckAnswer方法检查运算结果。其中利用random.nextInt()语句随机确定生成整数/分数、括号起始位置、运算符。
commonFactor方法用于求两数的最大公因数,用于化简分数。
3.3 CheckRepeat类
对满足要求的式子进行查重,并将不重复的式子存储到题集文件。
generate方法用于生成临时题库和答案,并将不重复的题目添加到题集和答案集中。其中调用createFormula方法随机生成式子暂存于临时题库中,调用ifRepeat方法判断式子是否重复。
3.4 CheckAnswer 类
对式子进行计算并校验结果,生成式子的逆波兰表达式数组。
checkout方法用于生成逆波兰表达式(即后缀表达式),调用caculate方法计算并校验结果,其中分数结果用greatFraction方法(调用Create类里的commonFactor方法求分子分母的最大公因数)化简返回最简分数。
package fourcalculations;
public String[] checkout(String formula,int length){
// 操作数 && 操作符 && 逆波兰表达式
Stack
Stack
String[] reversePolishNotation = 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();) {
//StringBuffer类中的方法主要偏重于对于字符串的变化,例如追加、插入和删除等,这个也是StringBuffer和String类的主要区别。
StringBuilder digit = new StringBuilder();
//将 式子 切割为 c字符
char c = formula.charAt(i);
//若 c字符 为10进制数字,将 c字符 加入digit(可以将多位数一起储存为一个数)
while (Character.isDigit(c)||c=='/'||c=='\'') {
digit.append(c);
i++;
c = formula.charAt(i);
}
if (digit.length() == 0){ //当前digit里面已经无数字,即当前处理符号
switch (c) {
//如果是“(”转化为字符串压入字符栈
case '(': {
stackOperator.push(String.valueOf(c));
break;
}
//遇到“)”了,则进行计算,因为“(”的优先级最高
case ')': {
//将 stackOperator 栈顶元素取到 operator
String operator = stackOperator.pop();
//当前符号栈里面还有 + - × ÷时,取 操作数 并 运算
while (!stackOperator.isEmpty() && !operator.equals("(")) {
//取操作数a,b
String a = stackNumber.pop();
String b = stackNumber.pop();
//后缀表达式变形
reversePolishNotation[j++] = a;
reversePolishNotation[j++] = b;
reversePolishNotation[j++] = operator;
//计算
String ansString = calculate(b, a, operator);
//如果 结果 不满足 要求 则 return -1,该式子不满足条件
if(ansString == null)
return null;
//将结果压入栈
stackNumber.push(ansString);
//符号指向下一个计算符号
operator = stackOperator.pop();
}
break;
}
//遇到了“=”,则计算最终结果
case '=': {
String operator;
//当前符号栈里面还有 + - × ÷时,即还没有算完,取 操作数 并 运算
while (!stackOperator.isEmpty()) {
//取值 && 取操作数
operator = stackOperator.pop();
String a = stackNumber.pop();
String b = stackNumber.pop();
//后缀表达式变形
reversePolishNotation[j++] = a;
reversePolishNotation[j++] = b;
reversePolishNotation[j++] = operator;
//计算
String ansString = calculate(b, a, operator);
if(ansString == null)
return null;
stackNumber.push(ansString);
}
break;
}
//不满足之前的任何情况
default: {
String operator;
//当前符号栈里面还有 + - × ÷时,取 操作数 并 运算
while (!stackOperator.isEmpty()) {
//当前符号栈,栈顶元素
operator = stackOperator.pop();
if (hashmap.get(operator) >= hashmap.get(String.valueOf(c))) { //比较优先级
//取值
String a = stackNumber.pop();
String b = stackNumber.pop();
//后缀表达式变形
reversePolishNotation[j++] = a;
reversePolishNotation[j++] = b;
reversePolishNotation[j++] = operator;
//计算
String ansString =calculate(b, a, operator);
if(ansString == null)
return null;
stackNumber.push(ansString);
}
else {
stackOperator.push(operator);
break;
}
}
stackOperator.push(String.valueOf(c)); //将符号压入符号栈
break;
}
}
}
//处理数字,直接压栈
else {
stackNumber.push(digit.toString());
//reversePolishNotation[j++] = digit.toString();
continue; //结束本次循环,回到for语句进行下一次循环,即不执行i++(因为此时i已经指向符号了)
}
i++;
}
//获取 栈顶数字 即 等式的最终答案
reversePolishNotation[length-3] = "=";
reversePolishNotation[length-2] = stackNumber.peek();
reversePolishNotation[length-1] = formula;
return reversePolishNotation;
}
3.5 problemSet类
生成并输出相关文本。
createProblemSet方法用于定义题目与答案所在数组,并调用createEXEFile方法、cresteAnsFile方法生成相应文本文件。
createGradeFile方法用于生成成绩文本文件,先获取指定的答题文件和答案文件中的内容,调用output方法,输出正确/错误的题目题号。
public void createProblemSet(int n,int r){
CheckRepeat temporarySet = new CheckRepeat();
ArrayList returnList = temporarySet.generate(n,r);
ArrayList
ArrayList
//获取题集、答案集
for (int i =0;i<2*n;i++) {
if(i<n) txtList.add(returnList.get(i).toString());
else ansList.add(returnList.get(i).toString());
}
//输出题集、、答案集
createEXEFile(txtList);
createAnsFile(ansList);
}
/**
-
生成并输出Exercises.txt
-
@param txtList 为 所得题集的 式子字符串
*/
private void createEXEFile(ArrayList txtList){
try{
File exTXT = new File("../Exercises.txt");//如果文件已存在,则删除文件 if (exTXT.exists()) { exTXT.delete(); } //创建文件成功?? if(exTXT.createNewFile()){ System.out.println("创建Exercises.txt:"); FileOutputStream txtFile = new FileOutputStream(exTXT); PrintStream q = new PrintStream(exTXT); q.println("学号:3220004889 姓名:迪力拜尔 成绩:\n"); for(int i=0;i<txtList.size();i++){ System.out.print(">"); q.println(i+1 + ". " +txtList.get(i)); //System.out.println(i+1 + ". " +txtList.get(i)); } txtFile.close(); q.close(); System.out.println("Exercises.txt 创建成功!"); }
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}
4.测试
4.1性能分析和内存分析
4.2测试运行
项目运行,输入参数后出现以下界面
题库文档 Exercises.txt:
答案文档 Answer.txt:
录入答题文件:
在命令行输入文件路径
生成成绩文件 Grade.txt
5.项目小结
坤杜孜阿依——————个人总结
本次软件工程的双人合作项目是我第一次和别人合作完成的项目。在本次的合作中发现了不少问题。首先是前期需求分析,结构设计时花费了较多的时间,双方的想法意见没有及时交流。具体打代码因为没试过双人合作写代码,在合作上稍显慌乱,好在在一段时间后逐渐适应,合作逐渐流畅,各部分模块的思路对接比较顺畅。经过本次团队合作项目后,我对双人合作的模式有更深入的了解,更明白高效的交流在合作中的重要性。
迪力拜尔——————个人总结
此次结对编程作业我主要负责的是代码复审、测试以及报告内容。本次合作主要问题出现在前期沟通上,导致代码整体框架的构造花费了较长时间,但我们在意识到问题之后及时对合作模式进行改进,选择线下面对面交流,大大提升了工作效率,但在代码实现方面仍有不足,用户交互也只是简单地通过命令行方式,没有做出图形界面。
6.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 25 |
Estimate | 估计这个任务需要多少时间 | 300 | 250 |
Development | 开发 | 70 | 70 |
Analysis | 需求分析 (包括学习新技术) | 60 | 65 |
Design Spec | 生成设计文档 | 45 | 30 |
Design Review | 设计复审 | 50 | 45 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 60 | 65 |
Design | 具体设计 | 60 | 60 |
Coding | 具体编码 | 100 | 100 |
Code Review | 代码复审 | 30 | 25 |
Test | 测试(自我测试,修改代码,提交修改) | 50 | 60 |
Reporting | 报告 | 40 | 30 |
Test Repor | 测试报告 | 30 | 20 |
Size Measurement | 计算工作量 | 20 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 715 | 955 | 935 |