MathExam—— 在原有的V2.0.0版本上加入三年级四则混合运算题
- 211606335 吴沂章
- 211606318 林锃寒
一、预估与实际
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 5 | 3 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 90 | 720 |
• Design Spec | • 生成设计文档 | 20 | 20 |
• Design Review | • 设计复审 | 10 | 30 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 10 |
• Design | • 具体设计 | 30 | 20 |
• Coding | • 具体编码 | 360 | 600 |
• Code Review | • 代码复审 | 20 | 300 |
• Test | • 测试(自我测试,修改代码,提交修改) | 50 | 20 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 30 | 20 |
• Size Measurement | • 计算工作量 | 30 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1793 |
二、需求分析
特点1
- 运算符在2~4个
特点2
- 减法运算的结果不能有负数
特点3
- 除法运算除数不能为0,不能有余数
特点4
- 可以加括号
经过分析,我认为,这个程序应当:
一个式子中应要添加多个运算符
计算符号判断,生成随机数
通过调度场和逆波澜算法来实现运算
三、设计
1. 设计思路
第一步实现四则运算题目的生成
第二步实现字符串写入文本
第三步实现从命令行接受参数并传入程序运行
第四步实现 中缀表达式 ------转换------> 后缀表达式
第五步实现对后缀表达式的计算方法
最后修改若干Bug,规范代码名,完善代码,提升质量
...
- 代码类图
- 逆波兰函数流程图:
2. 实现方案
准备工作:先在Github上创建仓库,克隆到本地,创建一个Pair文件夹
在MyEclipse中创建一个类,包含主方法和各函数
准备工作:先在Github上创建仓库,克隆到本地。
技术关键点:
如何令用户在输入 -n n -grade grade 和 -grade grade -n n 的时候都可以成功运行。
如何在运算方法中实现"()"的优先运算
如何在四则运算中添加括号。
如何实现逆波澜算法
如何记录错题。
...
四、编码
- 本次代码未实现以下功能:
- 除数为0的错误
- 差值为负值的错误
- 四则运算题目的数值的范围为[0,10),不能是2位数
1. 调试日志
- 日志一:
- 没有考虑定义的运算符乘号小写字母"x"在入栈时被判定为符合要求的字符,导致判定出错
private static boolean isOperator(String operator){if (operator.equals("+")||operator.equals("-")||operator.equals("×")||operator.equals("÷")||operator.equals("(")||operator.equals(")")) {return true;}return false;
}
- 解决方案:将所有乘法中的符号统一定义为输入法中的“×”
无对应的解决方案代码
- 日志二:
- 生成三年级题目时调用i值导致抛异常为空值
for (int j = 0; j < count; j++) {....省略部分代码str_ArithmeticProblem[i] = "( " +n1 + " " + cs[c1] + " " + n2 +" ) " + " " + cs[c2] + " " + n3;....省略部分代码
}
- 解决方案:将对应的循环值j放入str_ArithmeticProblem[j]中
for (int j = 0; j < count; j++) {....省略部分代码str_ArithmeticProblem[j] = "( " +n1 + " " + cs[c1] + " " + n2 +" ) " + " " + cs[c2] + " " + n3;....省略部分代码
}
2. 关键代码
调度场算法
- 逆波兰函数
//调度场算法——[中缀表达式转后缀表达式]private void toPostfixExpression(String str_mix){int len = str_mix.length();char c,nextChar;String sc;for (int i = 0 ; i <= len-1 ; i++) {c = str_mix.charAt(i); sc = String.valueOf(c);if(isOperator(sc)) //判断是否是操作符{if(operators.isEmpty()){ //判断为空栈,入栈operators.push(sc);} else {if(priority(operators.peek()) < priority(sc) && !sc.equals(")")){ //栈顶操作符优先级小于当前操作符优先级且操作符不为右括号,入栈operators.push(sc);} else if(priority(operators.peek()) >= priority(sc) && !sc.equals(")")){while(!operators.empty() && !operators.peek().equals("(") //栈不为空,当前栈顶操作符不为左括号&& priority(operators.peek()) >= priority(sc)){ //操作符优先级小于等于当前栈顶操作符优先级do {operator_Add = operators.pop();postfixExpression.append(operator_Add);operand.push(operator_Add);} while (false); } // 栈顶操作符是左括号时停止压栈operators.push(sc); //否则直接入栈} else if(sc.equals(")")){ //当前扫描到的操作符为右括号(不做入栈操作),依次压栈相匹配的左括号内容do {operator_Add = operators.pop();postfixExpression.append(operator_Add);operand.push(operator_Add);} while (!operators.peek().equals("("));operators.pop(); //弹出栈顶无用操作符左括号}}}else { //非操作符if(!sc.equals(" ")){postfixExpression.append(sc);operand.push(sc); }}}while(!operators.empty()){ //结束字符串扫描后操作符的栈不为空则则压栈operator_Add = operators.pop();postfixExpression.append(operator_Add);operand.push(operator_Add);}}//逆波兰函数private int reversePolish() {// TODO Auto-generated method stubchar c;int len = postfixExpression.toString().length();for (int i = 0; i < len; i++) {c = postfixExpression.charAt(i);if(!isOperator(String.valueOf(c))){ //判断非操作符,入栈postfixNumber.push(Integer.parseInt(String.valueOf(c)));} else{int m = postfixNumber.pop();int n = postfixNumber.pop();String operator = String.valueOf(c);postfixNumber.push(Calculation(n, m, operator)); }}return postfixNumber.pop();}
3. 代码规范
本次实验使用的代码规范:
类名使用 UpperCamelCase 风格
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,遵循驼峰形式
变量和常量的命名方式:
- 非公有(private/protected/default)变量前面要加上小写m
- 静态变量(static)前面加上小写s
- 其它变量以小写字母开头
- 静态常量(static final)全大写
类型与中括号紧挨相连来定义数组
大括号的使用约定。如大括号内为空,则简介地写成{}即可,不需要换行;如果是非空代码块则:
- 左大括号前不换行
- 左大括号后换行
- 右大括号前换行
- 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行
左小括号和字符之间不出现空格;同样的,有小括号和字符之间也不出现空格
4. 结对编程的过程
我们采取的是Ping-Pong式的结对方式
最初先是一起讨论这次项目的编码流程,研究用何种方式实现效果,并模拟程序运行得出这次的代码难点有哪些
然后针对代码需要实现的功能进行分工,确立两个人的任务
编程过程中互相交换意见,同步进程
最后融合代码,并对代码进行复审
五、测试
序号 | 测试代码 | 预期输出结果 | 实际输出结果 |
---|---|---|---|
1 | java MathExam -n 10 -grade 1 | 输出10道一年级算术题 | 和预期结果相符 |
2 | java MathExam -n 10 -grade 2 | 输出10道二年级算术题 | 和预期结果相符 |
3 | java MathExam -n 10 -grade 3 | 输出10道三年级算术题 | 和预期结果相符 |
4 | java MathExam -grade 3 -n 10 | 输出10道三年级算术题 | 和预期结果相符 |
5 | java MathExam -grade 2 -n 10 | 输出10道二年级算术题 | 和预期结果相符 |
6 | java MathExam -grade 1 -n 10 | 输出10道一年级算术题 | 和预期结果相符 |
7 | java MathExam -g 3 -n 10 | 不符合参数类型输入规范,结束程序 | 和预期结果相符 |
8 | java MathExam -grade 3 -c 10 | 不符合参数类型输入规范,结束程序 | 和预期结果相符 |
9 | java MathExam 10 -grade 1 | 不符合参数类型输入规范,结束程序 | 和预期结果相符 |
10 | java MathExam -g 10 -c 1 | 不符合参数类型输入规范,结束程序 | 和预期结果相符 |
六、总结
- 随着题目要求难度越来越高,所写的代码也越来越长,一定要多写注释提醒自己
- 先对本次项目进行需求分析,避免重复修改代码时产生不必要的bug
- 结对编程的过程中,跟自己的搭档要商量好各自的任务,若是各写各的,一人一个版本,会给后续的代码融合增加很大的难度
- 结对编程中一个人写完了自己的任务,也不要自顾自的放松,这是两个人的项目,应该帮助自己的搭档
- 结对编程中与队友一起探讨代码中无数bug也是很快乐的
- 这次代码难度还是很高的,还好我们彼此之间相互鼓励,相互帮助,总算是能给客户看了