第二周(2017-5-121 23:59截止)输出阶段总结博客
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) | |
---|---|---|---|---|
Planning | 计划 | 20 | 30 | |
Estimate | 估计这个任务需要多少时间 | 300 | 480 | |
Development | 开发 | |||
Analysis | 需求分析 (包括学习新技术) | 20 | 70 | |
Design Spec | 生成设计文档 | |||
Design Review | 设计复审 (和同事审核设计文档) | 20 | 20 | |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 0 | 0 | |
Design | 具体设计 | 30 | 30 | |
Coding | 具体编码 | 100 | 240 | |
Code Review | 代码复审 | 60 | 90 | |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 100 | |
Reporting | 报告 | |||
Test Report | 测试报告 | |||
Size Measurement | 计算工作量 | 300 | 480 | |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 | |
合计 | 480 |
需求分析
完成简单的四则运算。
其中包括:
1.统计题目数
2.正确生成题目
3.正确判题
4.正确率统计 5分
设计思路
第一周:
1. 统计题目数需要从输入中提取。 2. 加减乘除的符号用随机数除以4取余定义。 3. 题目的产生用随机数产生并用一个循环来产生。 4. 正确判题需要将输入的答案与正确答案比较是否相等。 5. 正确率统计需要定义一个变量为正确的题数,正确的题数/总题数即为正确率。
第二周:
考虑了更为复杂的四则混合运算,本来的思路是先随机生成表达式,然后中缀表达式转后缀并对后缀表达式求值。
但是在实现过程中,发现我们暂时想不出如何随机生成一个混合运算,于是考虑先生成后缀表达式,再把后缀表达式转为中缀作为题目,直接对后缀表达式求值。
最终实现了随机生成后缀表达式并求值,但是关于后缀转中缀的代码逻辑却存在一些问题。
关于后缀转中缀,我的思路如下:
定义一个栈用来存储数字
从左到右扫描后缀表达式,遇到数字进栈
遇到操作符将栈顶的两个数字出栈与操作符结合继续进栈,重复操作,直到栈内为空
其中若是后一个操作符的优先级大于前一个操作符,那么前一个操作符连接的数字运算左右两端需要加括号
UML类图如下:
实现过程中的关键代码解释
输入题目数
System.out.println("小朋友,你想做几道题呀,说出你的愿望吧:"); Scanner scan = new Scanner(System.in); int num = scan.nextInt();
生成运算符
this.a = (int) (Math.random () * 100); this.b = (int) (Math.random () * 100); this.c = generator.nextInt ( 99 ) + 1; switch ((int) (Math.random () * 4)) { case 0: op = '+'; result = a + b; break; case 1: op = '-'; result = a - b; break; case 2: op = '*'; result = a * b; break; case 3: op = '/'; if (a % c == 0) result = (a / c); else result = (int) (a / c); result1 = (float) (a / c); result2.setter ( a, c ); result2 = result2.reduce_deno ( result2 ); break;
循环实现题目的输出
while(flag == 1){ int correct_num = 0; int_formula[] intf; intf = new int_formula[num]; for(int i = 0;i<(int )(num/2);i++){ int_formula ex = new int_formula(); int timushu =i+1; System.out.printf("第"+timushu+"题: "); ex.setFormula(); intf[i]=ex; Scanner ss = new Scanner(System.in); String getinput=ss.nextLine(); while(getinput.indexOf('/')== 1){ answer2 = answer2.parseFraction(getinput); if(!answer2.isLeagal()){ System.out.println("分母不能为0!"); } getinput=ss.nextLine(); } if(ex.getOp()=='/'){ if (!ex.result2.isInteger()) { answer = Integer.parseInt(getinput); answer1 = (float)answer; answer2 = answer2.parseFraction(getinput); } else { answer1 = (float)answer; answer2 = answer2.parseFraction(answer); } } else{ answer = Integer.parseInt(getinput); } if(ex.getOp()!='/'){ if(ex.result==answer){ correct_num++; System.out.println("正确,很棒啊,再接再厉"); } else System.out.println("这道题有点难,正确答案是:"+ex.result+" 继续加油哦"); } else{ if((ex.result==answer&&ex.result2.isInteger())||ex.result1==answer1||ex.result2.equals(answer1)){ correct_num++; System.out.println("正确,很棒啊,再接再厉"); } else System.out.println("错啦,正确答案是:"+ex.result+" 继续加油哦"); } }
判断正误
if(ex.getOp()!='/'){ if(ex.result==answer){ correct_num++; System.out.println("正确,很棒啊,再接再厉"); } else System.out.println("这道题有点难,正确答案是:"+ex.result+" 继续加油哦"); } else{ if((ex.result==answer&&ex.result2.isInteger())||ex.result1==answer1||ex.result2.equals(answer1)){ correct_num++; System.out.println("正确,很棒啊,再接再厉"); } else System.out.println("错啦,正确答案是:"+ex.result+" 继续加油哦");
正确率统计
System.out.printf("不错嘛,共%d题,做对%d题,正确率:%.2f",num,correct_num,accuracy*100 ); System.out.println('%'); System.out.println("想要学的更好就输入你还想做的题目数,如果想休息了输入0"); num=scan.nextInt();
后缀转中缀
import java.util.*; import java.util.stream.Collectors; /** * Created by think on 2017/5/21. */ //思路: //定义一个栈用来存储数字 //从左到右扫描后缀表达式,遇到数字进栈 //遇到操作符将栈顶的两个数字出栈与操作符结合继续进栈,重复操作,直到栈内为空 //其中若是后一个操作符的优先级大于前一个操作符,那么前一个操作符连接的数字运算左右两端需要加括号 public class Change { //后缀转中缀 /** * 提前将 符号的优先级定义好 */ private static final Map <Character, Integer> basic = new HashMap <Character, Integer> (); static { basic.put ( '-', 0 ); basic.put ( '+', 0 ); basic.put ( '*', 1 ); basic.put ( '/', 1 ); } public void test() { String s3 = new String (); String a = toSuffix ( s3 ); //传入一串随机生成的后缀表达式 System.out.println ( a ); System.out.println ( new dealEquation ( a ) ); } /** * 将 后缀表达式 转化为 中缀表达式 */ public String toSuffix(String infix) { List <String> queue = new ArrayList <String> (); //定义队列 用于存储 运算符 以及最后的 中缀表达式 List <String> stack = new ArrayList <> (); //定义栈 用于存储 数字 最后stack中会被 弹空 char[] charArr = infix.trim ().toCharArray (); //字符数组 用于拆分数字或符号 String standard = "*/+-"; //判定标准 将表达式中会出现的运算符写出来 char ch = '&'; //在循环中用来保存 字符数组的当前循环变量的 这里仅仅是初始化一个值 没有意义 int len = 0; //用于记录字符长度 【例如100*2,则记录的len为3 到时候截取字符串的前三位就是数字】 for (int i = 0; i < charArr.length; i++) { //开始迭代 ch = charArr[i]; //保存当前迭代变量 if (Character.isDigit ( ch )) { //如果当前变量为 数字 len++; } else if (Character.isLetter ( ch )) { //如果当前变量为 字母 len++; } else if (Character.isSpaceChar ( ch )) { //如果当前变量为 空格 支持表达式中有空格出现 if (len > 0) { //若为空格 代表 一段结束 ,就可以往队列中 存入了 【例如100 * 2 100后面有空格 就可以将空格之前的存入队列了】 stack.add ( String.valueOf ( Arrays.copyOfRange ( charArr, i - len, i ) ) ); //往 栈存入 截取的 数字 len = 0; //长度置空 } continue; //如果空格出现,则一段结束 跳出本次循环 } else if (standard.indexOf ( ch ) != -1) { //如果是上面标准中的 任意一个符号 if (len > 0) { //长度也有 stack.add ( String.valueOf ( Arrays.copyOfRange ( charArr, i - len, i ) ) ); //说明符号之前的可以截取下来做数字 len = 0; //长度置空 } if (Character.isDigit ( ch )) { //如果是数字 stack.add ( String.valueOf ( ch ) ); //将数字 放入栈中 continue; //跳出本次循环 继续找下一个位置 } if (!stack.isEmpty ()) { //如果栈不为empty int size = stack.size () - 1; //获取栈的大小-1 即代表栈最后一个元素的下标 boolean flag = false; //设置标志位 while (size >= 0 && standard.indexOf ( ch ) != -1) { //若当前ch为符号,则 栈里元素从栈顶弹出两个数字 for (int k = 0; k < 3; k++) { stack.remove ( stack.size () - 1 ); size--; //size-- 保证下标永远在栈最后一个元素【栈中概念:指针永远指在栈顶元素】 flag = true; //设置标志位为true 表明一直在取()中的元素 } while (size >= 0 && !flag && basic.get ( queue.get ( size ) ) >= basic.get ( ch )) { //若取得不是()内的元素,并且当前栈顶元素的优先级>=对比元素 那就出栈插入队列 stack.add ( String.valueOf ( queue.remove ( size ) ) ); //同样 此处也是remove()方法,既能得到要获取的元素,也能将栈中元素移除掉 size--; } } } if (i == charArr.length - 1) { //如果已经走到了 后缀表达式的最后一位 if (len > 0) { //如果len>0 就截取数字 stack.add ( String.valueOf ( Arrays.copyOfRange ( charArr, i - len + 1, i + 1 ) ) ); } int size = stack.size () - 1; //size表示栈内最后一个元素下标 while (size >= 0) { //一直将栈内 符号全部出栈 并且加入队列中 【最终的中缀表达式是存放在队列中的,而栈内最后会被弹空】 stack.add ( String.valueOf ( stack.remove ( size ) ) ); size--; } } } } return queue.stream ().collect ( Collectors.joining ( "," ) );//将队列中元素以,分割 返回字符串 } public String dealEquation(String equation){ String [] arr = equation.split(","); //根据, 拆分字符串 List<String> list = new ArrayList<String>(); //用于计算时 存储运算过程的集合【例如list中当前放置 100 20 5 / 则取出20/5 最终将结果4存入list 此时list中结果为 100 4 】 for (int i = 0; i < arr.length; i++) { //此处就是上面说的运算过程, 因为list.remove的缘故,所以取出最后一个数个最后两个数 都是size-2 int size = list.size(); switch (arr[i]) { case "+": double a = Double.parseDouble(list.remove(size-2))+ Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(a)); break; case "-": double b = Double.parseDouble(list.remove(size-2))- Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(b)); break; case "*": double c = Double.parseDouble(list.remove(size-2))* Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(c)); break; case "/": double d = Double.parseDouble(list.remove(size-2))/ Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(d)); break; default: list.add(arr[i]); break; //如果是数字 直接放进list中 } } return list.size() == 1 ? list.get(0) : "运算失败" ; } }
存入文件
public class Histroy { String qstr; String astr; String str; public List<String> qstrlist = new ArrayList<String>(); public List<String> astrlist = new ArrayList<String>(); public void scan(String qstr,String astr){ str=(astr+" "+qstr); } public void Histroy_create(){ String path = "d:\\History"; File f = new File(path); if(!f.exists()){ f.mkdirs(); String fileName="histroy.txt"; File file = new File(f,fileName); if(!file.exists()){ try { file.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void Histroy_save(){ FileWriter writer; String fileName = ("d:\\History\\histroy.txt"); try { writer = new FileWriter(fileName, true); writer.write(str); writer.write("\r\n"); writer.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void Histroy_read() throws FileNotFoundException{ Scanner in = new Scanner(new File("d:\\History\\histroy.txt"));//为历史信息.tit这个File创建一个扫描器in while(in.hasNextLine()){ String line = in.nextLine();//读出历史信息.txt的下一行 Scanner lineScanner = new Scanner(line);//为每一行建立一个扫描器 lineScanner.useDelimiter(" ");//使用空格作为分隔符 qstr = lineScanner.next();//问题 astr = lineScanner.next();//答案 qstrlist.add(qstr); astrlist.add(astr); } in.close(); } }
运行过程截图
代码托管地址
遇到的困难及解决方法
- 除法是一个比较困难的点,因为除法涉及到除数不能等于0的问题,由于是简单的四则运算,都是整数计算,所以我将除数定义为1到100的整数,
this.c = generator.nextInt ( 99 ) + 1;
就可以避免讨论除数的问题。 我们暂时想不出如何随机生成一个混合运算,于是考虑先生成后缀表达式,再把后缀表达式转为中缀作为题目,直接对后缀表达式求值。
最终实现了随机生成后缀表达式并求值,但是关于后缀转中缀的代码逻辑却存在一些问题。对结对的小伙伴做出评价
结对是整体做出一个框架,然后两人讨论进行修改,都是非常努力,一遍又一遍修改,尝试了后缀转中缀,讨论了存入文件,讨论了很多的方法,尽力实现项目的要求,可能是水平受限,最后实现的不太尽如人意,但是大部分的内容都实现了。