MathExamLv2——周世元211606348,许燕婷211606338
结对编程
211606348 周世元 211606338 许燕婷
一、预估与实际
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 100 | |
• Estimate | • 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 30 | 30 |
• Design Spec | • 生成设计文档 | 30 | 70 |
• Design Review | • 设计复审 | 10 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 20 |
• Design | • 具体设计 | 20 | 20 |
• Coding | • 具体编码 | 500 | 300 |
• Code Review | • 代码复审 | 20 | 100 |
• Test | • 测试(自我测试,修改代码,提交修改) | 200 | 300 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 40 | 100 |
• Size Measurement | • 计算工作量 | 15 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 50 | 100 |
合计 | 1170 |
二、需求分析
小学一年级数学
· 数学一年级下学期 第五单元测试题
· 两位数减两位数 不退位减
· 两位数减两位数(退位减)
· 两位数加两位数(进位加)
· 两位数加两位数(不进位加)
· 两位数减一位数(退位)
· 两位数加一位数(进位)
· 两位数减一位数、整十数(不退位)
· 小括号
· 两位数加一位数、整十数(不进位)
· 整十数加、减整十数
· 整十数加、减整十数
小学二年级数学
· 万以内的加法和减法
· 求比一个数多几的应用题
· 不连续进位加
· 连续进位加
· 加法的验算
· 万以内的加法和减法
我通过百度的方式了解到,小学三年级数学有如下的几个特点:
- 特点
- 只有正整数运算
- 没学负数,不会小数运算
- 只会运算1000以内的数
- 小学二年级除了要满足一年级的要求,还要算仅限于九九乘法表的乘法与除法
- 小学三年级进行四则混合运算,运算符在2~4个之间,可以加括号,减法运算的结果不能有负数,除法运算除数不能为0,不能有余数。
输入格式:
- 程序需从命令行中接收两个参数 年级 和 题目数量,分别用 -n 和 -grade 表示,这两个参数的顺序不限,当 n 为 2 或 1 时,分别代表给 2 年级,1 年级出题
输出格式:
- 把题目和标准答案写入out.txt文件,输出的格式要求
- 每道题目占一行
- 每道题的格式:
- (1)题号:数字 + 一对英文括号;
- (2)题目:数字、符号之间空一格;
- (3)题号与题目之间也需要加一空格,行末不需要加空格。
- (4)题目和标准答案分开输出,先输出所有题目,后输出所有答案,答案的输出格式与题目一样。
经过分析,我认为,这个程序应当:
- 可接收2个参数:题目数量、年级
- 会检测出各种输入参数的异常
- 会根据传入的年级生成对应需求的题目
- 最后将题目答案输出到out.txt文件
- 采用代码重构和分而治之的思想。
- 各部分函数代码给注释,有明确的逻辑。
- 程序运行中、文件输出后要给明确的提示
三、设计
1. 设计思路
-
这个程序有2个类,7个函数,功能如下:
-
主类和内部类
- 内部类:Node (用来存储后缀表达式)
-
主函数
- 主函数里调用check()函数排除输入参数的各种问题
- 主函数里判断当前输入参数的年级,根据年级执行不同的题目生成器
- 主函数里根据对应的年级调用init3()函数初始化三年级题目和答案
- 主函数里根据对应的年级调用init1_2()函数初始化1、2年级题目和答案
- 主函数里调用OutPutFile()函数将题目和答案输出到out.txt文件中
-
check()函数用来排除输入参数的各种问题,函数流程如下:
- 判断输入的参数的长度(只能为4)
- 判断是否用-n 和-grade作为标识符
- 取出参数的前导0(类似0000001的情况)
- 检查参数题目数量必须都是数字
- 检测年级参数是否是1-3
- 判断题目的数量是否为正整数
-
init3()函数用来生成题目和答案,函数流程如下:
- 生成3个可变字符串
- 随机生成2-4个运算符(必须有2个不同)
- for循环生成题目
- 随机在+、-运算符左右边的数字分别生成2个括号
- 将随机生成的题目丢用逆波兰表达式算出答案并保存
- 将题目和答案拼接成字符串
-
init1_2()函数用来生成题目和答案,函数流程如下:
-直接引用1.2年级的题目生成器 -
CalPoland()函数
- 实现逆波兰表达式
-
ShuntYardAlgo()函数
- 实现调度场算法
-
OutPutFile()函数
- 将题目和答案输出到out.txt文件
-
函数流程图:
-
算法的关键:
- 判断输入的参数是否合法,对于不合法的各种情况需给予提示
- 生成带括号的四则运算的题目是否满足教学大纲的要求
2. 实现方案
写出具体实现的步骤
- 准备工作:
- 先在Github上Fork厂库,并克隆到本地
- 编写MathExam.java
- 在三年级题目的基础上改进逻辑并增加1.2年级出题功能
- 重构代码,并增加注释
- 代码测试
- 技术关键点:
- 生成带括号的四则运算
- 拼接可变字符串
- 按需求产生随机数
- 按需求产生随机运算符和括号
- 输入参数的各种判断
- 逆波兰表达式
- 调度场算法
- 文件io流
四、编码
编码过程:
-
main函数:
- 调用check()函数判断输入参数是否有异常
- 没有异常的情况下判断年级
- 如果是1.2年级就将4个当前输入的参数,删除2个标识符其余封装在一个数组中传给init1_2()函数中进行1.2年级题目初始化.并调用outPutFile()函数输出out.txt文件。
- 如果是3年级就直接调用init3()函数中进行3年级题目初始化.并调用outPutFile()函数输出out.txt文件。
-
init3()函数:
- 创建1个可变字符串str1用于拼接答案
- 创建1个for循环
- 循环内生成2个随机数
- 用str拼接题目
- 用条件判断语句判断当前的运算符并算出答案
- str1拼接答案
- str1拼接到str尾部
-
init1_2()函数:
- 和作业1的init()函数功能相同
遇到的问题:
- 带括号的四则运算如何生成
查阅别人的博客并没有找到带括号的四则运算。然后我就先试着生成随机的没有括号的四则运算,完成任务后我再考虑如何加上括号,设计了好久终于加了一些if判断并且生成了带括号的四则运算了。
- 如何算出题目的答案
1. 调试日志
- ShuntYardAlgo()函数,字符串传入报数组跃界,字符串转字符传入不会报错。
解决:这是我本次开发浪费最多时间调试的bug。原因知道后其实很简单,字符串转字符我没用str.toCharrArrays()这个方法,而是自己开辟一个char[100],默认舒适化是0。当遍历字符串末尾的时候自然就跃界报错了。
- 如何生成带括号的四则运算
解决:当时在这个地方也困扰了我很久,一步步的增加判断,随机安排括号,由于不好控制括号出现的位置我调试了很久才生成带括号的四则运算
2. 关键代码
public static StringBuffer init3(String[] args) throws Exception {
int m = 1;
int l = 1;
String str = null;
// 生成2个字符串用于拼接题目和答案
StringBuffer strbuf = new StringBuffer();
StringBuffer strbuf1 = new StringBuffer();
StringBuffer strbuf2 = new StringBuffer();
// random用于随机生成题目中的操作数
int random = 0;
// int flag=5;
// 将四种运算符放入数组
String[] strop = { "+", "-", "x", "÷" };
// 将随机生成的2-4个运算符存入ostr数组
String[] ostr = new String[4];
int w = Integer.valueOf(args[args[0].equals("-n") ? 1 : 3]);
for (int i = 0; i < w; i++) {
// 随机生成的2-3个运算符
int operator = (int) (Math.random() * 3) + 2;
int k = 0;
// 随机生成运算符号(至少有2种不同运算符号)
for (int j = 0; j < operator; j++) {
k = (int) (Math.random() * 4);
ostr[j] = strop[k];
if (operator == 2 && j == 1) {
// 控制在只有2个运算符的情况下2个运算符号一定不同
while (ostr[0] == ostr[1]) {
k = (int) (Math.random() * 4);
ostr[0] = strop[k];
}
}
}
int flag1 = 1;
int flag = 1;
for (int j = 0; j < operator + 1; j++) {
if (operator == 4)
// 如果运算符有+-就自动生成括号运算符
if (j < operator && (ostr[j].equals("+") || ostr[j].equals("-")) && flag == 1) {
strbuf.append("( ");
flag = 0;// 判断是否加了左括号0代表已近加了
flag1 = 0;// 判断是否可以加右括号了
}
random = (int) (Math.random() * 101);
if (j == operator) {
// 最后一个随机数的末尾不加空格
if (flag == 0 && flag1 == 1) {
strbuf.append(random + " )");
flag1 = 0;
flag = 100;
} else {
strbuf.append(random);
if (flag == 0) {
// 在最后一个数后面加上右括号
flag1 = 1;
}
}
} else {
if (flag == 0 && flag1 == 1) {
strbuf.append(random + " )" + " ");
flag1 = 0;
flag = 100;
} else {
strbuf.append(random + " ");
if (flag == 0) {
// 在下一次循环当中可以加入右括号
flag1 = 1;
}
}
}
if (j < operator)
strbuf.append(ostr[j] + " ");
}
l = 1;
// 重新初始化数组
ostr = new String[4];
//
MathExam mathExam = new MathExam();
mathExam.Init();
str = strbuf.toString();
char[] charArray = str.toCharArray();
double shuntYardAlgo = mathExam.ShuntYardAlgo(charArray);
String vf = String.valueOf(shuntYardAlgo);
boolean matches = vf.matches("\\d+.[0]?");
if (matches) {
// strbuf2用于拼接题目
strbuf2.append("(" + m + ")" + " " + strbuf + "\r\n");
// strbuf1用于拼接答案
strbuf1.append("(" + m + ")" + " " + strbuf + " " + "=" + " " + (int) shuntYardAlgo + "\r\n");
m++;
l = 0;
}
strbuf = new StringBuffer();
if (l != 0)
i--;
}
strbuf2.append("\r\n");
strbuf2.append(strbuf1);
return strbuf2;
}
3. 代码规范
请给出本次实验使用的代码规范:
- 第一条代码中的命名均不以下划线或美元符号开始,也不一下划线或美元符号结束。
- 第二条代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
- 第三条类名使用UpperCamelCase风格,但是以下情形例外:DO / DTO / VO / AO / PO 等。
- 第四条方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵循驼峰形式。
- 第五条变量和常量的命名方式:非公有(private/protected/default)变量前面要加上小写m;
- 第六条类型与中括号紧挨相连来定义数组。正例:定义整形数组 int[] arrayDemo;并人工检查代码是否符合规范
- 第七条杜绝完全不规范的缩写,避免忘文不知义。反例:AbstractClass“缩写”命名成AbsClass;condition“缩写”命名成 condi,此类随意缩写严重降低了代码的可阅读性。
- 第八条为了达到代码自己自解释的目标,任何自定义编程元素在命名时,使用精良完整的单词组合来表达其意。但是有些名称实在过于长,这个可以适当的缩写,不要忘文不知义就可以。这个不是客观的规定。
- 第九条不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。
- 第十条大括号的使用约定。如果是大括号内为空,则简介地写成{}即可,不需要换行;如果是非空代码块则:
- 左大括号前不换行。
- 左大括号后换行。
- 右大括号前换行。
- 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。
五、测试
| 测试 | 预期结果 | 实际结果 |
| --------------------------------------- | -------------------------------- | -------- | -------- |
| 不输入参数 | -grade和-n标识符错误!! | -grade和-n标识符错误!!|
|只输入一个参数:100 | -grade和-n标识符错误!! | -grade和-n标识符错误!!|
| -n 10 -grade 1 | 题目已经生成,详情请见out.txt | 题目已经生成,详情请见out.txt |
| -n 10.5 -grade 1 | 请输入正整数 | 请输入正整数 |
| -n ascc -grade 2 | 请输入正整数 | 请输入正整数 |
| -n 10 -grade vsdv | 年级参数错误,只能在[1~3]以内 | 年级参数错误,只能在[1~3]以内 |
| -n 00001 -grade 3 | 题目已经生成,详情请见out.txt | 题目已经生成,详情请见out.txt |
| -n 1000 -grade 2.3 | 年级参数错误,只能在[1~3]以内 | 年级参数错误,只能在[1~3]以内 |
| -n 10 -grade 002 | 题目已经生成,详情请见out.txt | 题目已经生成,详情请见out.txt |
| -n 10000 -grade 3 | 题目已经生成,详情请见out.txt | 题目已经生成,详情请见out.txt |
| -n -1 -grade 3 | 请输入正整数 | 请输入正整数 |
| 1000 -n -grade 2 | -grade和-n标识符错误!! | -grade和-n标识符错误!! |
| -n 10 -grade -3 | 年级参数错误,只能在[1~3]以内 | 年级参数错误,只能在[1~3]以内 |
| -n 1000 2 -grade | -grade和-n标识符错误! | -grade和-n标识符错误! |
| -grade 2 -n 1000 | 题目已经生成,详情请见out.txt | 题目已经生成,详情请见out.txt |
| -grade 0.1 -n 800 | 年级参数错误,只能在[1~3]以内 | 年级参数错误,只能在[1~3]以内 |
| -grade a1 -n 10 | 年级参数错误,只能在[1~3]以内 | 年级参数错误,只能在[1~3]以内 |
| -grade 001 -n 20 | 题目已经生成,详情请见out.txt | 题目已经生成,详情请见out.txt |
| -grade 1 -n 0000000002 | 题目已经生成,详情请见out.txt | 题目已经生成,详情请见out.txt |
| -grade 0.1 -n 0.1 | 请输入正整数 | 请输入正整数 |
| -n a1 -grade a1 | 请输入正整数 | 请输入正整数 |