结对编程项目-四则运算 整体总结博客

时间:2023-02-22 08:41:11

结对编程项目-四则运算 总结博客

结对小组组员:20175310 20175317

一、题目要求

实现一个命令行程序,要求:

  • 自动生成小学四则运算题目(加、减、乘、除)
    ·支持整数
    ·支持多运算符(比如生成包含100个运算符的题目)
    ·支持真分数
  • 统计正确率
  • 第一周要求输出整数/多运算符(题目生成/题目运算判题)
  • 第二周(2017-5-21 23:59截止)输出整体总结博客

二、需求分析

根据题目要求,本周项目需要达到的内容主要有以下几个方面:
1、自动生成分数
2、能够进行带分数式子的中缀式转后缀式
3、能正确进行计算并输出最终结果
4、能够读取用户输入的分数并与正确结果进行比较

三、设计思路

1、首先,我们在上周的结对过程中将所有的类都放入了同一个.java文件,在修改过程中我们意识到这种编写方式会耗费太多时间来寻找具体的类,所以我们首先将之前的类区分开来,放到了不同的.java文件中以便后续修改。

2、自动生成分数

①表达式的产生:

random产生随机数,共需要三个随机数,其一用于决定生成的式子中是否含有分数,第二个随机数用于分子,第三个随机数用于分母。
参考教材中的代码生成一个新的类Rational,用于设定分数的分子分母并进行分数的加减乘除运算。
调用Rational将第一部生成的随机数读入分数中并进行计算的返回结果。

②表达式的输出:

使用循环输出,在整数式子的基础上进行修改,同样要保证数的个数比运算符多一个:


if (j <= a) {
                        fuhao k = new fuhao();  //声明类fuhao的对象k
                        char q = k.yunsuanfu();   //产生一个运算符q
                        Question1 = Question1 + zi + '/' + mu +" " +q+" ";  //将生成的随机数和随机符号存入Question1中
                    } 
else {
                        Question1 = Question1 +zi + '/' + mu;    //将生成的随机数存入Question1中
                    }

3、计算自动生成的含分数表达式的结果

系统生成的表达式已经存入了字符串Question1中,下一步需要将字符串转化为后缀式并求值,这样才能与用户输入的数字比对,判断是否正确.我们参考了老师提供的代码,新建了一个MyDC类,用于将带分数的中缀式转化为后缀式并调用Rational类中的方法进行计算:

public class MyDC
{
    /** constant for addition symbol */
    private final char ADD = '+';
    /** constant for subtraction symbol */
    private final char SUBTRACT = '-';
    /** constant for multiplication symbol */
    private final char MULTIPLY = '*';
    /** constant for division symbol */
    private final char DIVIDE = '÷';
    /** the stack */
    private Stack<String> stack;//存放操作数的栈
    private Rational  op1, op2;   //r1,r2

    public MyDC()   {
        stack = new Stack<String>();
    }

    public String evaluate (String expr)
    {
        String r1, r2, result = null;
        String token;
        StringTokenizer tokenizer = new StringTokenizer (expr);  //划分表达式
        while (tokenizer.hasMoreTokens())
        {
            token = tokenizer.nextToken();//将算数表达式分解

            if (isOperator(token))  //见下方isOperateor方法,当是运算符的时候进入if语句
            {
                r2 = stack.pop();
                op2=tranIntoRational(r2);
                r1 = stack.pop();
                op1=tranIntoRational(r1);
                result = evalSingleOp (token.charAt(0), op1, op2);//见下方evaSingleOp方法
                result= stack.push (result);//将计算结果压栈
            }
            else {
                stack.push (token);//操作数入栈
            }

        }

        return result;//输出结果
    }

    private boolean isOperator (String token)//判断是否为运算符,注意用equal语句比较字符串
    {
        return ( token.equals("+") || token.equals("-") ||
                token.equals("*") || token.equals("÷") );
    }

    private String evalSingleOp (char operation, Rational op1, Rational op2)   //分数运算
    {
        Rational result=new Rational();
        result.setNumerator(0);
        result.setDenominator(1);
        switch (operation)
        {
            case ADD:
                result = op1.add(op2);
                break;
            case SUBTRACT:
                result = op1.sub(op2);
                break;
            case MULTIPLY:
                result = op1.muti(op2);
                break;
            case DIVIDE:
                result = op1.div(op2);
                break;
            default:
                System.out.println("Error!");
        }
        return result.toString();
    }

    public Rational tranIntoRational (String s){   //将String类型转换成Rational类型
        Rational r = new Rational();
        String token1, token2;
        StringTokenizer tokenizer1 = new StringTokenizer(s, "/");//把操作数以"/"为标记分割开来
        token1 = tokenizer1.nextToken();    //分子
        if (tokenizer1.hasMoreTokens()) {//如果有第二个元素就把token1放在分子的位置,token2放在分母的位置
            token2 = tokenizer1.nextToken();    //分母

            r.setNumerator(Integer.parseInt(token1));   //设置分子
            r.setDenominator(Integer.parseInt(token2));   //设置分母
        }
        else {//如果没有第二个元素就把token1放在分子的位置,分母固定为1
            r.setNumerator(Integer.parseInt(token1));
            r.setDenominator(1);
        }
        return r;
    }
}


4、判断用户输入的答案是否和正确答案相符

该步修改较为简单,我们将上周double类型的right改为了String类型的dui,将正确答案以String类型读入,并调用equals()方法将用户输入的字符串与正确答案进行比对:

                String dui ="";   //代表正确答案dui
                dui =  dui + evaluator.evaluate(Question);
                Scanner reader2 = new Scanner(System.in);
                String answer = reader2.nextLine();     //读取用户输入的答案
                if(answer.equals(dui)){
                    System.out.println("回答正确!");
                    count++;      //count代表正确题数
                }
                else if(!answer.equals(dui)){
                    System.out.println("回答错误!正确答案是:" + dui);
                }

5、计算正确率

该步并没有太大修改,与上周一样,在程序开头定义int型变量count并将其初值定为0,如果计算正确在输出正确时的输出时count++,整个循环结束之后,(正确题数count÷总题数x×100)%就是正确率lv

float lv=count*100/x;     //正确率
System.out.println("测试结束!正确率为:"+lv+"%");

四、UML类图

结对编程项目-四则运算 整体总结博客

五、运行过程截图

结对编程项目-四则运算 整体总结博客

六、代码托管地址

https://gitee.com/xicyannn/20175310xcy/tree/master/src/%E7%BB%93%E5%AF%B9/%E5%9B%9B%E5%88%99%E8%BF%90%E7%AE%97-%E7%AC%AC%E4%BA%8C%E5%91%A8

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

  • 问题1:
    在写代码时发现需要把中缀式转成后缀式后,将每个分数和运算符拆开,下图中等号左边表示系统自动生成的中缀式,右边是后缀式。可以看出中缀转后缀并没有出错,问题在于后缀式的第一项是1/6,第二项是1/7,通过StringTokenizer方法拆分后缀式,但由于1/6和1/7中间没有任何标识符,因此系统自动把他们俩当成了一个整体,所以在之后的入栈时就会提示出错:Exception in thread main java. lang NumberFormatException: For input string: 1/61/7,意思就是说 数字格式异常:对于输入字符串:1/61/7
    结对编程项目-四则运算 整体总结博客

  • 问题1解决方案:
    通过在分数以及运算符前后加空格作为间隔的标志,下面是在运算符后面加空格的代码:

if (j <= a) {
                        fuhao k = new fuhao();  //声明类fuhao的对象k
                        char q = k.yunsuanfu();   //产生一个运算符q
                        Question1 = Question1 + zi + '/' + mu +" " +q+" ";  //将生成的随机数和随机符号存入Question1中
                    } else {
                        Question1 = Question1 +zi + '/' + mu;    //将生成的随机数存入Question1中
                    }

下面是在分数后面加空格的代码:

if(ch >= '0' && ch <= '9' && ch1 == '/'&&ch2>='0'&&ch2<='9') {// 如果是分数,则直接输出
                out+=ch;
                out+=ch1;
                out+=ch2;
                out+=" ";
                continue;
            }

修改这两部分的代码后,运行结果如下图。可以看到不管是运算符还是分数,中间都有空格间隔。

结对编程项目-四则运算 整体总结博客

  • 问题2:
    当分母为1时显示的不是整数形式而是x/1(x是大于1的整数),并且判断对错时,对于输入的整数答案判定为错误,只有输入x/1才显示正确。

结对编程项目-四则运算 整体总结博客

  • 问题2解决方案:
    在输出结果的时候加一个判断,当分子为0时,结果为0,分母为1时结果为分子,其余情况正常输出,代码如下:

i

f (numerator == 0) {
            result = "0";
        }
        else {
            if (denominator == 1)
                result = numerator + "";
            else
                result = numerator + "/" + denominator;
        }
        return result;

八、对结对的小伙伴做出评价

这周的编程过程中我们配合明显比之前好得多,我们在代码编写过程中进行了分工并各司其职。在我们各自的代码都编写完后我们一起进行了代码整合,出现问题后他负责逐步调试查找问题,我负责解决问题修改代码,他也会提出一些有效的解决方法,使我们的项目得以顺利进行。博客的编写我们也进行了分工,这使得我们的学习效率大大提升了。总的来说我的伙伴是一个优秀的合作伙伴。

九、PSP

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 10 15
Development 开发
· Analysis · 需求分析 (包括学习新技术) 30 60
· Design Spec · 生成设计文档 20 20
· Design Review · 设计复审 (和同事审核设计文档) 10 15
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 30
· Design · 具体设计 30 30
· Coding · 具体编码 120 300
· Code Review · 代码复审 30 50
· Test · 测试(自我测试,修改代码,提交修改) 30 60
Reporting 报告
· Size Measurement · 计算工作量 10 15
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 15 25
合计 325 620