软件工程第四次作业—四则运算[结对刘成志]
四则运算
要求1 参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,博客是分别完成的)。
(1) 给出每个功能的重点、难点、编程收获。
(一)public double caculate(String str)
这个函数是本程序中的主要计算函数,通过对输入的字符串进行解析然后判断操作符或是数值然后计算大致为:
重点:
1.判断string当中有没有非法字符 。
2.循环开始解析字符串,当字符串解析完,且符号栈为空时,则计算完成 。
3.用当前取得的运算符与栈顶运算符比较优先级:若高于,则因为会先运算,放入栈顶;若等于,因为出现在后面,所以会后计算,所以栈顶元素出栈,取出操作数运算; 若小于,则同理,取出栈顶元素运算,将结果入操作数栈。
4.判断当前运算符与栈顶元素优先级,取出元素,进行计算(因为优先级可能小于栈顶元素,还小于第二个元素等等,需要用循环判断。
难点:优先级比较以及栈的进出
收获:了解了堆栈对于四则运算中符号栈以及数值栈的存放情况,通过不同的优先级实现栈内元素的出入从而实现四则运算功能。
public double caculate(String str) { // 1.判断string当中有没有非法字符 String temp;// 用来临时存放读取的字符 // 2.循环开始解析字符串,当字符串解析完,且符号栈为空时,则计算完成 StringBuffer tempNum = new StringBuffer();// 用来临时存放数字字符串(当为多位数时) StringBuffer string = new StringBuffer().append(str);// 用来保存,提高效率 while (string.length() != 0) { temp = string.substring(0, 1); string.delete(0, 1); // 判断temp,当temp为操作符时 if (!isNum(temp)) { // 1.此时的tempNum内即为需要操作的数,取出数,压栈,并且清空tempNum if (!"".equals(tempNum.toString())) { // 当表达式的第一个符号为括号 double num = Integer.parseInt(tempNum.toString()); numStack.push(num); tempNum.delete(0, tempNum.length()); } // 用当前取得的运算符与栈顶运算符比较优先级:若高于,则因为会先运算,放入栈顶;若等于,因为出现在后面,所以会后计算,所以栈顶元素出栈,取出操作数运算; // 若小于,则同理,取出栈顶元素运算,将结果入操作数栈。 // 判断当前运算符与栈顶元素优先级,取出元素,进行计算(因为优先级可能小于栈顶元素,还小于第二个元素等等,需要用循环判断) while (!compare(temp.charAt(0)) && (!priStack.empty())) { double a = numStack.pop();// 第二个运算数 double b = numStack.pop();// 第一个运算数 char ope = priStack.pop(); double result = 0;// 运算结果 switch (ope) { // 如果是加号或者减号,则 case '+': result = b + a; // 将操作结果放入操作数栈 numStack.push(result); break; case '-': result = b - a; // 将操作结果放入操作数栈 numStack.push(result); break; case '*': result = b * a; // 将操作结果放入操作数栈 numStack.push(result); break; case '/': if(a!=0){ result = b/a; }else{ exception(); } numStack.push(result); break; } } // 判断当前运算符与栈顶元素优先级, 如果高,或者低于平,计算完后,将当前操作符号,放入操作符栈 priStack.push(new Character(temp.charAt(0))); if (temp.charAt(0) == ')') {// 当栈顶为'(',而当前元素为')'时,则是括号内以算完,去掉括号 priStack.pop(); priStack.pop(); } } else // 当为非操作符时(数字) tempNum = tempNum.append(temp);// 将读到的这一位数接到以读出的数后(当不是个位数的时候) } return numStack.pop(); }
(二)private boolean isNum(String temp)
这个函数主要的功能就是判断传入的字符是不是0-9的数字
/** * 判断传入的字符是不是0-9的数字 * * @param str * 传入的字符串 * @return */ private boolean isNum(String temp) { return temp.matches("[0-9]"); }
(三) private boolean compare(char str)
这个函数是本程序中的比较操作符优先级的函数,大致思想如下:
重点:
1.比较当前操作符与栈顶元素操作符优先级,如果比栈顶元素优先级高,则返回true,否则返回false
2.str 需要进行比较的字符
3.比较结果 true代表比栈顶元素优先级高,false代表比栈顶元素优先级低
4. '('优先级最高, ')'优先级最低, '*/'优先级只比'+-'高
难点:设计运算符的优先级,从而对应栈内的存放顺序。
收获:了解计算机堆栈对于四则运算中符号的处理方式。
/** * 比较当前操作符与栈顶元素操作符优先级,如果比栈顶元素优先级高,则返回true,否则返回false * * @param str 需要进行比较的字符 * @return 比较结果 true代表比栈顶元素优先级高,false代表比栈顶元素优先级低 */ private boolean compare(char str) { if (priStack.empty()) { // 当为空时,显然 当前优先级最低,返回高 return true; } char last = (char) priStack.lastElement(); // 如果栈顶为'('显然,优先级最低,')'不可能为栈顶。 if (last == '(') { return true; } switch (str) { case '#': return false;// 结束符 case '(': // '('优先级最高,显然返回true return true; case ')': // ')'优先级最低, return false; case '*': { // '*/'优先级只比'+-'高 if (last == '+' || last == '-') return true; else return false; } case '/': { if (last == '+' || last == '-') return true; else return false; } // '+-'为最低,一直返回false case '+': return false; case '-': return false; } return true; }
(四)public static void exception()
重点:
这个函数主要是为了处理异常输入时候不至于打断函数报错的异常处理函数,比如除数不能为0,当随机出题的时候遇到/0这种形式的时候就会调用exception函数并输出一个提示语句然后进行正常出题,这样可以保证在出现异常输入时候整个程序还可以继续回调执行。
难点:try catch的合理应用。
收获:对于稳定程序要通过try catch尽量捕获可能出现的异常。
public static void exception() { ArrayList<Object> s = new ArrayList<Object>(); ArrayList<Object> s1 = new ArrayList<Object>(); ArrayList<Object> s2 = new ArrayList<Object>(); Random random = new Random(); int num[] = new int[4]; char[] cs = { '+', '-', '*', '/' }; char[] kuo = { ' ', '(' }; for (int i = 0; i < 4; i++) { num[i] = (random.nextInt(10) + 1); } for (int i = 0; i < num.length; i++) { char cz = cs[(int) (Math.random() * cs.length)];// 随机操作运算 s.add(num[i]); if (i < num.length - 1) s.add(cz); } for (int i = 0; i < s.size(); i++) { char kuohao = kuo[(int) (Math.random() * kuo.length)]; if (s.get(i) instanceof Integer && i < s.size() - 1) { s1.add(kuohao); s1.add(s.get(i)); } else if (s.get(s.size() - 1) instanceof Integer) s1.add(s.get(i)); } int temp = 0; int p = 0; int temp1 = 0; int temp2 = 0; for (int i = 0; i < s1.size(); i++) { if (!(s1.get(i).equals('('))) { s2.add(s1.get(i)); } if (s1.get(i).equals('(')) { p++; if (p == 2) { temp1 = temp; } if (p == 3) { temp2 = temp1; } temp = i; s2.add(s1.get(i)); } if ((temp < s1.size() / 2 && i == ((int) s1.size() - temp - 1) && s1.get( temp).equals('(')) || (temp > s1.size() / 2 && i == ((int) s1.size() - (temp - (s1 .size() / 2))) && s1.get(temp) .equals('('))) { s2.add(')'); } if (p == 2) if ((temp1 < s1.size() / 2 && i == ((int) s1.size() - temp1 - 1) && s1 .get(temp1).equals('(')) || (temp1 > s1.size() / 2 && i == ((int) s1.size() - (temp1 - (s1 .size() / 2))) && s1.get( temp1).equals('('))) { s2.add(')'); } if (p == 3) if ((temp2 < s1.size() / 2 && i == ((int) s1.size() - temp2 - 1) && s1 .get(temp2).equals('(')) || (temp2 > s1.size() / 2 && i == ((int) s1.size() - (temp2 - (s1 .size() / 2))) && s1.get( temp2).equals('('))) { s2.add(')'); if ((temp1 < s1.size() / 2 && i == ((int) s1.size() - temp1 - 1) && s1 .get(temp1).equals('(')) || (temp1 > s1.size() / 2 && i == ((int) s1.size() - (temp1 - (s1 .size() / 2))) && s1 .get(temp1).equals('('))) { s2.add(')'); } } } int demp = -1; for (int i = 0; i < s2.size(); i++) { for (int j = i + 1; j < s2.size(); j++) { if ((demp + 1) == j) { Collections.swap(s2, demp, j); } if (s2.get(i).equals(' ') && s2.get(j).equals(')')) demp = j; } } f4 operate = new f4(); String str = s2.toString(); str = str.substring(1, str.length() - 1); str += '#'; String ss = str.replaceAll(",", ""); String ss1 = ss.replaceAll("\\s*", "");// 可以替换大部分空白字符, 不限于空格 // \s // 可以匹配空格、制表符、换页符等空白字符的其中任意一个 double t2 = operate.caculate(ss1); System.out.println(ss1.substring(0, ss1.length() - 1) + "=" + "\t\t" + doubletransint(t2)); }
(五)public static String doubletransint(double num)
重点:
这个函数主要是将double型输出结果(如12.0)输出时转换为int型数据(如12)。
收获:对于函数中整形的数据值,我们要把它从double变为int输出,以便用户观看。
public static String doubletransint(double num){ if(num % 1.0 == 0){ return String.valueOf((long)num); }else{ BigDecimal b = new BigDecimal(num); double f1 = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); return String.valueOf(f1); } }
(2)给出结对编程的体会
(一)结对编程的好处:
1.结对编程会遇到不同编程者的想法,可能自己编程时候遇到的一个问题,结对伙伴突然提出另一种思路,实验后确实可行。
2.结对编程增强代码的质量,并有效的减少BUG。
3.结对编程可以在编程中相互讨论,可能更快更有效地解决问题。
(二)结对编程的缺点:
- 对于有不同习惯的编程人员,一起工作会产生麻烦,甚至矛盾。比如在编程语言和编程分格习惯上有差异。
- 遇到问题,结对一方或许会对一个问题各执己见(代码风格可能会是引发技术人员口水战的地方),争吵不休,反而产生重大内耗
- 两个人在一起工作可能会出现工作精力不能集中的情况。结对成员可能会交谈一些与工作无关的事情,反而分散注意力,导致效率比单人更为低下。
(三)总结:
通过列举结对编程的优缺点,我们可以得出结对编程对于编程水平、编程分格、编程思想以及编程经验相近的人来说是有很大帮助作用的。但是如果某一方在某一块产生了比较大的差距,那么或许相对来说结对编程没有单人编程完成的更迅速。
(3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。
1.编程语言。编程语言对于整个编程来讲是首要要讨论的话题,对于我所掌握的java和刘成志掌握的python。基于基础问题,我们在后期选择使用Java语言进行编译。
2.对于解决除数为0。在double中如果除数为0会出现无穷大值,在这一块的处理上我们争论使用变量的类型。
3.随机括号。在随机括号方面,我们有争论是否将括号和四则运算放到一起进行随机。
4欢迎界面。刚开始为了美观,我两在界面设置上有争论,前期设置成switch选择型,通过输入不同数值跳转不同功能,后期发现不符合规则,所以修改了。
5.如何计算带括号的运算方法,通过设定符号优先级以及栈的使用。
要求2 给出照片1张,包括结对的2位同学、工作地点、计算机,可选项包括其他能表达结对编程工作经历的物品或场景。 (5分)
结对同学:陈建宇,刘成志
工作地点:宿舍
计算机:笔记本
要求3 使用coding.net做版本控制。checkin 前要求清理 临时文件、可执行程序,通常执行 build-clean可以达到效果。(5分)
链接地址:https://git.coding.net/MR__Chen/f4.git