一、项目简介
实现一个自动生成小学四则运算题目的命令行程序
二、项目实现情况
1. 使用 -n 参数控制生成题目的个数(实现)
2. 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围(实现)
3. 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。(实现)
4. 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。(实现)
5. 每道题目中出现的运算符个数不超过3个。(实现)
6. 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。(实现)
7. 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件。(实现)
8. 程序应能支持一万道题目的生成。(实现)
9. 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计(实现)
三、PSP表格
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
30 |
85 |
· Estimate |
· 估计这个任务需要多少时间 |
30 |
52 |
Development |
开发 |
1520 |
1650 |
· Analysis |
· 需求分析 (包括学习新技术) |
30 |
50 |
· Design Spec |
· 生成设计文档 |
52 |
56 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
82 |
85 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
85 |
85 |
· Design |
· 具体设计 |
62 |
65 |
· Coding |
· 具体编码 |
62 |
85 |
· Code Review |
· 代码复审 |
95 |
85 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
74 |
75 |
Reporting |
报告 |
85 |
86 |
· Test Report |
· 测试报告 |
82 |
96 |
· Size Measurement |
· 计算工作量 |
52 |
52 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
85 |
89 |
合计 |
1338 |
2356 |
四、解题思路
对表达式的多种情况进行了分析
限制自动生成的表达式的形式与规模
规定括号在表达式中的插入位置
根据题目规定,不能出现负数和假分数,对不符合条件的减法和除法运算式,减号变为除号,反之除号变为减号
依次生成每一条表达式时,检索是否与前面生成的表达式重复
五、设计实现过程
六、代码说明
总共有三个类
number类
// 分子 分子的 public class number { private int numerator,denominator; public number() { // TODO Auto-generated constructor stub numerator=0; denominator=1; } public number (String str) { int pos=str.indexOf('/'); if(pos==-1) { numerator=Integer.parseInt(str); denominator=1; } else { numerator=Integer.parseInt(str.substring(0, pos).trim()); if(pos!=str.length()-1) denominator=Integer.parseInt(str.substring(pos+1).trim()); else denominator=1; } } public number(int a) { // TODO Auto-generated constructor stub numerator = a; denominator = 1; } public number(int a, int b){ numerator = a; denominator = b; } public void set(int a) { numerator = a; denominator = 1; } public void set(int a,int b) { numerator = a; denominator = b; } public String toString() { check(); if (numerator == 0) { return "0"; } else{ if (denominator == 1){ return numerator+""; } else{ return "("+numerator+"/"+denominator+")"; } } } public void check() { if(numerator<0&&denominator<0||numerator>0&&denominator<0) { numerator=-1*numerator; denominator=-1*denominator; } } public boolean equals(number n2) { check(); n2.check(); number x1=this.yuefen(); number x2=n2.yuefen(); if(x1.numerator==x2.numerator&&x1.denominator==x2.denominator) return true; else return false; } public number yuefen() { check(); number ans=new number(0); if(numerator==0||denominator==0) return ans; int divisor;//最大公约数 divisor = gcd(denominator, numerator); ans.denominator = denominator / divisor; ans.numerator = numerator / divisor; return ans; } public number add(number n2){ number re=new number(); int divisor;//最大公约数 re.denominator = denominator * n2.denominator; re.numerator = numerator * n2.denominator + n2.numerator * denominator; divisor = gcd(re.denominator, re.numerator); re.denominator = re.denominator / divisor; re.numerator = re.numerator / divisor; re.check(); return re; } private int gcd(int n1,int n2) { return n2==0?n1:gcd(n2, n1%n2); } public double ParseDouble(){ return numerator*1.0/denominator; } public number sub(number n2){ number re=new number(); int divisor;//最大公约数 re.denominator = denominator * n2.denominator; re.numerator = numerator * n2.denominator - n2.numerator * denominator; if (re.numerator != 0){ divisor = gcd(re.denominator, re.numerator); re.denominator = re.denominator / divisor; re.numerator = re.numerator / divisor; } re.check(); return re; } public number mul(number n2){ number re=new number(); int divisor;//最大公约数 re.denominator = denominator * n2.denominator; re.numerator = numerator * n2.numerator; if (re.numerator != 0){ divisor = gcd(re.denominator, re.numerator); re.denominator = re.denominator / divisor; re.numerator = re.numerator / divisor; } re.check(); return re; } public number div(number n2) { number re=new number(); int divisor;//最大公约数 if(numerator==0) return new number(0); re.numerator=numerator*n2.denominator; re.denominator=denominator*n2.numerator; if (re.numerator != 0){ divisor = gcd(re.denominator, re.numerator); re.denominator = re.denominator / divisor; re.numerator = re.numerator / divisor; } re.check(); return re; } }
question类
/*支持分数和随机长度,并可以将每次答题情况记录到文件里 */ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.Scanner; public class Question { public static String str = "";// 题目 public static String strNum;// 键盘 接收题目 public static int num = 1;// 每题中数的个数 默认 1 从键盘获取 题目的个数 public static int num_i = 0;// 题目中已有数的个数 public static int numberRange = 100;// 运算中数的最大取值 从键盘 获取 public static number sum = new number();// 结果 @SuppressWarnings("resource") public static void main(String[] args) { // 答案放在目录 String file = "Exercises.txt"; String ansfile = "Answers.txt"; String connfileString = null; String woronfileString = null; int[] a = new int[100000];// 放正确结果的数组 int[] b = new int[100000];// 错误结果的数组 int ai = 0; int bi = 0; System.out.println("---请根据提示输入数据 ---"); System.out.println("Myapp.exe -n 题目的个数"); System.out.println("Myapp.exe -r 数值的范围"); System.out.println("Myapp.exe -r 数值的范围"); System.out.println("Myapp.exe -e <记录正确题目的>.txt -a <记录错误题目的>.txt"); Scanner input = new Scanner(System.in); strNum = input.next(); // 题目的个数 if (strNum.equals("Myapp.exe")) { strNum = input.next(); if (strNum.equals("-n")) { num = input.nextInt(); System.out.println("输入的数据:" + num); } } strNum = input.next(); if (strNum.equals("Myapp.exe")) { strNum = input.next(); if (strNum.equals("-r")) { numberRange = input.nextInt(); System.out.println("输入的数据 范围为:" + numberRange); } } strNum = input.next(); if (strNum.equals("Myapp.exe")) { strNum = input.next(); if (strNum.equals("-e")) { connfileString = input.next(); connfileString = connfileString.replaceAll("<", ""); connfileString = connfileString.replaceAll(">", ""); } strNum = input.next(); if (strNum.equals("-a")) { woronfileString = input.next(); woronfileString = woronfileString.replaceAll("<", ""); woronfileString = woronfileString.replaceAll(">", ""); } } PrintStream out = null; PrintStream ansout = null; PrintStream eout = null; PrintStream aout = null; if (args.length >= 1) file = args[0]; try { out = new PrintStream(new FileOutputStream(file)); ansout = new PrintStream(new FileOutputStream(ansfile)); eout = new PrintStream(new FileOutputStream(connfileString)); aout = new PrintStream(new FileOutputStream(woronfileString)); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); input.close(); return; } // 题目 System.out.println("题目为:"); for (int i = 0; i < num;) { GetQuestion();// 题目 if (sum.toString().indexOf("-") == 0) { continue;// 去结果为负数的 } System.out.print(i + 1); System.out.println(":" + str + "请输入答案 : "); String answer = ""; answer = input.next(); System.out.print("正确答案: " + sum.toString() + " "); number re = sum.add(new number(10)); re = new number(answer); if (re.equals(sum)) { System.out.println("正确"); a[ai++] = i + 1; } else { System.out.println("错误"); b[bi++] = i + 1; } i++; out.println("四则运算题目" + i + str); ansout.println("答案" + i + sum.toString()); } out.println(); input.close(); out.close(); System.out.println(); int sum = ai; eout.print("Correct:" + sum + "("); for (int i = 0; i < ai; i++) { if (i == ai - 1) { eout.print(a[i]); } else { eout.print(a[i]+","); } } eout.print(")"); sum = bi; aout.print("Wrong:" + sum + "("); for (int i = 0; i < bi; i++) { if (i == bi - 1) { aout.print(b[i]); } else { aout.print(b[i]+","); } } aout.print(")"); } private static void GetQuestion() { str = ""; sum.set(0); ; num_i = (int) (Math.random() * 1) + 2; quesGrow(); } private static void quesGrow() { if (num_i > 1) { int j = num_i; num_i--; quesGrow(); int ck = (int) (Math.random() * 4); number w;// 随机产生的数字 if (ck != 0) w = new number(1 + (int) (Math.random() * numberRange)); else // 分母 w = new number(1 + (int) (Math.random() * numberRange), 1 + (int) (Math.random() * numberRange)); int t = (int) (Math.random() * 2); int f = (int) (Math.random() * 4); if (t == 0) { if (f == 0) { sum = sum.add(w); str = str + "+" + w.toString(); } if (f == 1) { sum = sum.sub(w); str = str + "-" + w.toString(); } if (f == 2) { if (j < 3) { sum = sum.mul(w); str = str + "*" + w.toString(); } else { sum = sum.mul(w); str = "(" + str + ")" + "*" + w.toString(); } } if (f == 3) { if (j < 3) { sum = sum.div(w); str = str + " / " + w.toString(); } else { sum = sum.div(w); str = "(" + str + ")" + " / " + w.toString(); } } } else { if (f == 0) { sum = sum.add(w); str = w.toString() + "+" + str; } if (f == 1) { if (j < 3) { sum = w.sub(sum); str = w.toString() + "-" + str; } else { sum = w.sub(sum); str = w.toString() + "-" + "(" + str + ")"; } } if (f == 2) { if (j < 3) { sum = sum.mul(w); str = w.toString() + "*" + str; } else { sum = sum.mul(w); str = w.toString() + "*" + "(" + str + ")"; } } if (f == 3) { if (j < 3) { sum = w.div(sum); str = w.toString() + " / " + str; } else { sum = w.div(sum); str = w.toString() + " / " + "(" + str + ")"; } } } } else if (num_i == 1) { int ck = (int) (Math.random() * 4); number w; if (ck != 0) w = new number(1 + (int) (Math.random() * numberRange)); else w = new number(1 + (int) (Math.random() * numberRange), 1 + (int) (Math.random() * numberRange)); sum = sum.add(w); str = str + w.toString(); } } }
test类
package zuoye; import javax.xml.stream.events.Namespace; public class test { public String nameString; public test() { nameString="<exercisefile>.txt"; System.out.println(nameString.indexOf("-")); } /** * @param args */ public static void main(String[] args) { test test = new test(); test.nameString = test.nameString.replaceAll("<", ""); test.nameString = test.nameString.replaceAll(">", ""); // 解析 System.out.println("tttt"+test.nameString);; } }
测试结果
八、项目小结
在结对编程的过程中,杨圣负责编辑代码,我提供了一些思路和进行对代码的测试,在设计过程中我们两人进行了诸多讨论和对于程序设计及运行后的bug进行了审查,对于程序我也进行了许多测试来验证,在这次结对编程过程中我们认为编程的优势主要就在于设计思路的共享,和我们互相为对方避免错误。总的来说,自己在编程时犯下的错误,有些尽管很简单,但却难以发现,而这些在另一个人眼中,却会十分明显。总而言之结对编程的经验让我们收获诸多。