结对编程练习_四则运算(第一周)

一、需求分析

实现一个命令行程序,要求:
自动生成小学四则运算题目(加、减、乘、除)

  • 支持整数
  • 支持多运算符(比如生成包含100个运算符的题目)
  • 支持真分数
  • 统计正确率
  1. 能生成随机数
  2. 产生的算式要有括号
  3. 要建立堆栈,进行中缀转后缀,以及后续后缀的运算
  4. 能输入想要产生的题目数
  5. 能输入用户计算的答案
  6. 能够比较用户输入的答案是否正确
  7. 能够统计用户答题的正确率

二、设计思路

  1. 生成一个有加减乘除支持括号的算式,以字符串的形式输出,每个操作数或操作符中间都用空格隔开。
    • 先生成一个不带括号的算式
    • 将生成的不带括号的算式随机插入括号
  2. 然后调用String类中的split方法,将字符串转化为字符串数组。
  3. 使用中缀表达式转后缀表达式规则将中缀表达式形式的字符串数组以后缀表达式的形式储存在堆栈中。
  4. 用后缀表达式计算规则进行计算,得出结果
  5. 得出的结果与用户输入结果进行比较
  6. 计算出正确率

三、实现过程中的关键代码解释

  • 代码1:
import java.util.Random;
int num = randnum.nextInt(100) + 2;
  • 功能:随机生成操作数的个数,包含100个运算符,记得后面一定要加2,因为操作数的个数一定大于等于2
  • 解析:
    输入图片说明
  • 代码2:
str = s.split(" ");
  • 功能:将字符串转化为字符串数组,以空格作为分隔符。
  • 解析:
    输入图片说明
  • 代码3:
for (int i = 0; i < str.length; i = i + 2) {
        choicechar = randnum.nextBoolean();
        if (choicechar && i != str.length - 1) { //产生左括号
            str[i] = "( " + str[i];
            countLBracket++;
        }
        choicechar = randnum.nextBoolean();
        if (choicechar && !str[i].startsWith("(") && countLBracket != 0) {
            str[i] = str[i] + " )";
            countLBracket--;
        }
    }
    for (int i = countLBracket; i > 0; i--)
        str[str.length - 1] = str[str.length - 1] + " )";

    for (String i : str)
        s2 = s2 + i + " ";

    return s2;
}
  • 功能:生成随机生成括号,括号位置正确,而且能支持嵌套。
  • 解析:
    • countLBracket是用来生成的左括号的个数
    • str[str.length - 1] = str[str.length - 1] + " )"当生成的左括号后面没有与之匹配的右括号时就在最后一个操作数后面把所有的右括号补全。
  • 代码4:
 while (tokenizer.hasMoreTokens()){
            token=tokenizer.nextToken();
            if (isOperator(token)){
                if (!OpStack.empty()){
                    if(judgeValue(token)>judgeValue(OpStack.peek()) && !token.equals(")") || token.equals("("))
                        OpStack.push(token);
                    else if (token.equals(")")){
                        //如果遇到一个右括号则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止
                        while (!OpStack.peek().equals("("))
                            output=output.concat(OpStack.pop()+" ");//弹出左括号上面的所有东西
                        OpStack.pop();//弹出左括号
                    }
                    else {
                        while (!OpStack.empty() && judgeValue(token)<=judgeValue(OpStack.peek())){
                            ////如果遇到其他任何操作符,从栈中弹出这些元素直到遇到发现更低优先级的元素或栈空为止
                            output=output.concat(OpStack.pop()+" ");
                        }
                        OpStack.push(token);
                    }
                }
                else
                    OpStack.push(token);//如果栈空则直接将遇到的操作符送入栈中,第一个不可能为右括号
            }
            else {
                output=output.concat(token+" ");//如果遇到操作数就直接输出
            }
        }
        while (!OpStack.empty()){
            //如果读到了输入分末尾,则将占中所有元素依次弹出
            output=output.concat(OpStack.pop()+" ");
        }
  • 功能:将中缀表达式转后缀表达式
  • 转化方法
    • 如果遇到操作数就直接输出。
    • 如果遇到操作符则将其放入栈中,遇到左括号也将其放入
      栈中。
    • 如果遇到右括号,则将栈元素弹出,将弹出的操作符输出直到遇到遇到左括号为止,注意左括号只弹出不输出。
    • 如果遇到任何其他操作符,如“+”,“-”,“*”,“÷”,“(”等,从栈中弹出元素直到遇到
      更低优先级的元素或栈空为止。弹出完这些元素才将遇到的操作符压入栈中。
    • 如果读到了输入的末尾,则将栈中所有元素依次弹出。
  • 解析:请看上方代码中注释。
  • 代码5:
 while (tokenizer.hasMoreTokens()){
            token=tokenizer.nextToken();
            if(isOperator(token)){
                op2=stack.pop();
                op1=stack.pop();
                result=calcSingle(op1,op2,token);
                stack.push(new Integer(result));
            }
            else {
                stack.push(new Integer(token));
            }
try {
……
}catch (ArithmeticException e){
    ……
}

四、UML类图

输入图片说明

五、运行结果截图

输入图片说明
输入图片说明

六、代码提交

码云链接: https://gitee.com/imjoking/PairWork

七、遇到的困难及解决方法

  • 问题1:如何生成随机数?

  • 解决方法:

    • 法一:用java.lang.Math中的random方法
    • 法二:用Rondom中的public int nextInt(int n)方法
  • 问题2:如何随机生成括号?

  • 解决方法:将‘(’和‘)’也放入操作符数组中,进行随机生成操作符。
    先生成不带括号的算式,然后随机在数字左边插入'(',接着随机插入')',个数和'('一样且不会出现在同一个数字两侧。

  • 问题3:运算符优先级不知道该如何比较

  • 解决方法:我原来想把加减乘除这几个符号封装成类,类中包含常量:char型保存符号,int型保存优先级,但是在操作过程中我发现要想实现多态就要使用上转型对象,但是上转型对象没法调用子类的成员变量。同时后面的case语句又要求char型必须是常量,整个程序搞在一起了,改了这里那里就不对了。后来我参考了学姐的博客才从圈里跳出来,再定义一个方法来比较优先级

private int calcSingle(int op1,int op2,String operation){
    int result=0;
    switch (operation){
        case "+":
            result=op1+op2;
            break;
        case "-":
            result=op1-op2;
            break;
        case "*":
            result=op1*op2;
            break;
        case "÷":
            result=op1/op2;
            break;
    }
    return result;
}
  • 问题4:生成的是头尾括号而且头尾括号所包含的内容是式子的全部内容。
  • 解决方法:此问题还未能解决。

八、结对感受

  • 结对编程真的很不符合我之前编程的习惯,我一直一个人敲代码,自己思考自己实现,现在发现只要有人看着我敲代码我就写不出来。
  • 我们两个人都很忙,有空的时间还错开来了,所以只能先分工完成。我负责计算部分,她负责生成算式部分。我向他提出要求,我的方法的接口是一个字符串,生成的算式应该是每个数字和运算符之间有一个空格。最后我们两个完成的了各自的部分以后,找时间互相检查对方的代码,然后写main方法把两边整合起来。我觉得两个人的合作一定要把对对方的期望说清楚。
  • 我觉得我的编程习惯还不错,命名习惯什么的都还行,但是我的结对伙伴的代码我看起来还真有点累,虽然写了注释。我觉得代码实现不算很难,优化代码的过程也是一种学习。
  • 总的来说基本上刚开始还是靠各自的编程能力在实现,但是后面的测试和优化还是体现了结对的好处的,可以提供不一样的思路,看到自己看不到的问题
  • 我认为需要改进的地方就是还是应该多交流,尽量凑在一起写代码,这样才是真正的结对编程。

九、PSP

PSP2.1 Personal Software Process Stages 预估耗时(小时/分钟) 实际耗时(小时/分钟)
Planning 计划 2小时 4小时
· Estimate · 估计这个任务需要多少时间 20小时 28小时半
Development 开发
· Analysis · 需求分析 (包括学习新技术) 4小时 2小时半
· Design Spec · 生成设计文档
· Design Review ·设计复审(和同事审核设计文档) 2小时 1小时
·Code Standard ·代码规范 半小时 50分钟
·Design ·具体设计 1小时 半小时
·Coding ·具体编码 5小时 8小时
·Code Review ·代码复审 2小时 5小时
·Test ·测试(自我测试,修改代码,提交修改) 1小时 2小时半
Reporting 报告 1小时 3小时
·Test Report ·测试报告
·Size Measurement ·计算工作量 半小时 半小时
·Postmortem&Process Improvement Plan ·事后总结,并提出过程改进计划 半小时 40分钟
合计 19小时半 28小时半

十、参考资料

posted @ 2019-04-04 22:53  MustaphaMond  阅读(256)  评论(0编辑  收藏  举报