软工作业-四则运算(java实现)BY叶湖倩,叶钰羽
BY-信安1班 叶湖倩(3216005170) 信安1班 叶钰羽(3216005171)
1. 项目介绍
源代码GitHub地址:https://github.com/yeyuyu/sizeyunsuan.git
该项目需求为实现一个自动生成小学四则运算题目的命令行程序,并且可以通过命令行参数控制生成题目的个数还有控制题中数值的范围,并满足一些其它的需求。我们的结对项目完成的需求如下:
是否实现 | |
---|---|
使用 -n 参数控制生成题目的个数 |
√ |
使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围 |
√ |
生成的题目中计算过程不会产生负数 | √ |
每道题目中出现的运算符个数不超过3个 | √ |
程序一次运行生成的题目不能重复 | √ |
当生成题目时,计算所有答案并存入执行程序的当前目录下 | √ |
程序支持10000道题目的生成 | √ |
判定答案对错并进行数量统计 | √ |
2. 耗时预计
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 75 |
· Estimate | · 估计这个任务需要多少时间 | 60 | 75 |
Development | 开发 | 1760 | 2165 |
· Analysis | · 需求分析 (包括学习新技术) | 90 | 80 |
· Design Spec | · 生成设计文档 | 60 | 45 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 90 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 45 |
· Design | · 具体设计 | 360 | 300 |
· Coding | · 具体编码 | 600 | 800 |
· Code Review | · 代码复审 | 360 | 600 |
· Test | · 测试(自我测试,修改代 | 200 | 150 |
Reporting | 报告 | 150 | 135 |
· Test Report | · 测试报告 | 60 | 45 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 1970 | 2375 |
3.解题思路
产生随机数-->生成表达式-->计算-->输出
4.设计实现过程
各类功能说明如下:
-
Main.class
主函数,调用其他功能模块实现-n,-r,-e命令。
-
CreateRandom.class
该类主要生成随机数,随机数分为分数,整数,若为分数则将其化简,实现数值和字符串之间的转换,并定义了分数的加减乘除。
-
CreateExpression.class
该类生成表达式。定义了四种操作符,随机取操作符,并调用CreateRandom.class取随机数,存入list数组,生成表达式 。
-
Calculate.class
该类主要实现计算功能。实现判负功能,并定义了表达式结果计算方法,即遇到括号,遇到连续的加减,乘除时的计算顺序。
-
OutPut.class
该类实现将表答式和答案在文件中输出,若已生成表达式文件,可实现将表达式与答案文件比对,输出比对结果。
5.代码说明
将数字的字符串形式转换为整数、分数形式
public CreateRandom(String string) { // 将数字的字符串形式转换为整数、分数形式 string = string.trim(); // 清除前后空格 int c = string.indexOf("'"); // 求带分数标志“'”的索引 int d = string.indexOf("/"); // 求分数标志“/”的索引 if (c != -1) { // 数字为带分数 int z = Integer.valueOf(string.substring(0, c)); //字符串子串 b = Integer.valueOf(string.substring(d + 1)); //取分母 a = z * b + Integer.valueOf(string.substring(c + 1, d)); //取分子 } else if (d != -1) { // 数字为真分数 b = Integer.valueOf(string.substring(d + 1)); a = Integer.valueOf(string.substring(0, d)); } else { // 数字为整数 a = Integer.valueOf(string); b = 1;} build(a, b);} //化简
2.将数字的整数、分数形式转换成字符串形式
// 数字形式转换为字符串形式 public String toString() { if (b == 1) { return String.valueOf(a); } else { int i = a / b; if (i != 0) { return String.format("%d'%d/%d", i, a - b * i, b); } else { return String.format("%d/%d", a, b);}}}
3.定义分数的加减乘除
// 加法 a + b public CreateRandom add(CreateRandom fraction) { return new CreateRandom(this.a * fraction.b + fraction.a * this.b, this.b * fraction.b);}
// 减法 a - b public CreateRandom subtraction(CreateRandom fraction) { return new CreateRandom(this.a * fraction.b - fraction.a * this.b, this.b * fraction.b);}
// 乘法 a x b public CreateRandom multiplication(CreateRandom fraction) { return new CreateRandom(this.a * fraction.a, this.b * fraction.b);}
// 除法a/b public CreateRandom division(CreateRandom fraction) { return new CreateRandom(this.a * fraction.b, b * fraction.a);}
-
Calculate.class
定义表达式计算优先级
public static String getResult(CreateExpression expression) { //定义表达式计算优先级 String string = expression.s; String[] strings = string.split("\\s+"); //分隔,并放进数组 List<String> list = new ArrayList<String>(); for (int i = 0; i < strings.length; i++) { list.add(strings[i]);} if (string.indexOf("(") != -1) { //定义括号的优先级 for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("=")) break; if (list.get(i).equals("(")) { list.set(i, calculate1(expression, list.get(i + 2), new CreateRandom(list.get(i + 1)), new CreateRandom(list.get(i + 3))) .toString()); for (int j = 0; j < 4; j++) { list.remove(i + 1);}}}} if (string.indexOf("×") != -1 || string.indexOf("÷") != -1) { //定义连续遇到乘除时的计算顺序 for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("=")) break; if (list.get(i).equals("×") || list.get(i).equals("÷")) { list.set(i - 1, calculate1(expression, list.get(i), new CreateRandom(list.get(i - 1)), new CreateRandom(list.get(i + 1))) .toString()); for (int j = 0; j < 2; j++) { list.remove(i);} i--;}}} if (string.indexOf("+") != -1 || string.indexOf("-") != -1) { //定义连续遇到乘除时的计算顺序 for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("=")) break; if (list.get(i).equals("+") || list.get(i).equals("-")) { list.set(i - 1, calculate1(expression, list.get(i), new CreateRandom(list.get(i - 1)), new CreateRandom(list.get(i + 1))) .toString()); for (int j = 0; j < 2; j++) { list.remove(i);} i--;}}} return list.get(0);}
-
OutPut.class
1.生成表达式输出文件
public void outputExercises() { try { PrintWriter exercisesWriter = new PrintWriter(new FileWriter("Exercises.txt"));//创建具有指定文件名称且不带自动行刷新 PrintWriter answerWriter = new PrintWriter(new FileWriter("Answers.txt")); for (int i = 1; i < titleNum + 1;) { //控制题目数量 CreateExpression expression = new CreateExpression(parameterRange); String getresult = Calculate.getResult(expression); String string = getresult; System.out.println(expression.isWrong == false); System.out.println(!(set.contains(string))); if (expression.isWrong == false && !(set.contains(string))) { set.add(string); exercisesWriter.println(i + ". " + expression.toString()); answerWriter.println((i + ". " + string)); i++;}} exercisesWriter.close(); answerWriter.close(); } catch (IOException e) { e.printStackTrace();} System.out.println("已经生成题目和答案");}
2.生成比对结果输出文件
public void outputGrade(String exercisePath, String answerPath, String gradePath) { try { BufferedReader exercisesReader = new BufferedReader(new FileReader(exercisePath)); BufferedReader answerReader = new BufferedReader(new FileReader(answerPath)); PrintWriter gradeWriter = new PrintWriter(new FileWriter(gradePath)); String exercises, answer; int c = 0, w = 0; StringBuilder correct = new StringBuilder("Correct: %d ("); StringBuilder wrong = new StringBuilder("Wrong: %d ("); while ((exercises = exercisesReader.readLine()) != null && (answer = answerReader.readLine()) != null) { int exercisesPoint = exercises.indexOf("."); //索引 int answerPoint = answer.indexOf("."); if (exercisesPoint != -1 && answerPoint != -1) { int i = Integer.valueOf(exercises.substring(0, exercisesPoint).trim()); CreateExpression expression = new CreateExpression(exercises.substring(exercisesPoint + 1)); CreateRandom ans = new CreateRandom(answer.substring(answerPoint + 1)); String getresult = Calculate.getResult(expression); String string = getresult; if (string.equals(ans.toString())) { c++; correct.append(" " + i); if (c % 30 == 0) { correct.append("\r\n");} } else { w++; wrong.append(" " + i); if (w % 30 == 0) { wrong.append("-r\n");}}}} gradeWriter.println(String.format(correct.append(" )").toString(), c)); gradeWriter.println(String.format(wrong.append(" )").toString(), w)); gradeWriter.flush(); exercisesReader.close(); answerReader.close(); gradeWriter.close(); } catch (IOException e) { e.printStackTrace();} System.out.println("已经比较完成,并生成输出文件");}
6.测试运行
- 输入参数-n为600,-r为100,窗口运行界面如下:
- 生成的Exercises.txt和Answers.txt部分截图如下:
- 输入-e Exercises.txt Answers.txt命令,窗口运行结果如下:
- 比对Exercises.txt和Answers.txt,输出的比对结果Rrade.txt如下:
- 故意写错几题答案,Grade.txt文件截图如下:
- 测试生成10000道题目,Exercises.txt文件部分截图:
7.项目小结
首次尝试结对编程,刚开始在讨论功能设计时很顺利,沟通时没有遇到什么问题,但在实际编码时却遇到了许多问题。一开始各自编写代码,没有注释,导致看对方的代码耗时长或者看不懂。在编码阶段,我们分别编写不同的模块,当各自的功能模块编写完之后,遇到各模块调用出错,出现死循环等的问题,导致最后DEBUG找逻辑问题了花费大量时间。在编码伊始,未统一变量名,后续调整浪费了许多时间。通过此次合作,我们学会了分工合作,明白了代码注释和有效沟通的重要性。