20162311 结对编程项目-四则运算 阶段总结

20162311 结对编程项目-四则运算 阶段总结

一、需求分析

  • 能随机生成n道题目,n由使用者输入
  • 支持整数的四则运算
  • 能够判断正误,错误时能提醒并输出正确答案
  • 能计算出正确率
  • 能多次生成题目,直到使用者选择退出

后续拓展的可能

  • 支持真分数
  • 题目去重
  • 支持多运算符

备注:这次只实现了单运算符,而且做整数除法时只能得到整数结果;如8 / 7结果为1。这周还没有解决这些问题,下周会继续学习并解决。

二、设计思路

Operand类用来生成两个10以内的随机数,作为操作数。设计一个IntQuestions类,其中实例化一个Operand对象,用来生成操作数,还包含四种方法,分别生成两个10以内随机整数的加、减、乘、除,这四种方法返回值都为String类型。还有一个Judgement类,用来判断输入的答案是否正确。NiToSuffix类是用来将中缀表达式转化为后缀表达式,运用了栈的方法。把生成的后缀表达式作为参数传入Judgement类中的evaluate()方法,从而判断结果是否正确。ResultTest是结果测试类,它依赖于之前的类,用于生成表达式,输出结果是否正确,输出正确率。

UML类图

20162311 结对编程项目-四则运算 阶段总结_第1张图片

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

  • 中缀表达式转化为后缀表达式
package Arithmetic;

import java.util.*;


/**
 * Created by Administrator on 2017/5/13.
 */
public class NifixToSuffix {
    private Stack stack;
    private List list;

    private String message,Message="";


    public NifixToSuffix() {
        stack = new Stack();
        list = new ArrayList();
    }

    public void conversion(String expr) {
        int op1, op2, result = 0;
        String token;
        StringTokenizer tokenizer = new StringTokenizer(expr);

        while (tokenizer.hasMoreTokens()) {
            token = tokenizer.nextToken();

            if (token.equals("("))
                stack.push(token);
            else if (token.equals("+") || token.equals("-")) {
                if(!stack.empty()) {
                    if (stack.peek().equals("(")){
                        stack.push(token);
                    }
             else if (stack.peek().equals("*") || stack.peek().equals("/"))
                        list.add(stack.pop());
                }
               else stack.push(token);
            }
            else if (token.equals("*") || token.equals("/")) {
                if(!stack.empty()){
                    if(stack.peek().equals("*")||stack.peek().equals("/")) {
                        list.add(stack.pop());
                    }
                }
                stack.push(token);
            }
            else if (token.equals(")")) {
                while (true) {
                    String A = stack.pop();
                    if (!A.equals( "("))
                        list.add(A);
                    else break;
                }
            }
            else list.add(token);
        }
        while (!stack.empty()) {
            list.add(stack.pop());
        }
        ListIterator li = list.listIterator();
        while (li.hasNext()) {
            Message += li.next() + " ";
            li.remove();
        }
        message = Message;

    }

    public String getMessage() {
        return message;
    }
}

转换规则

  • 设立一个栈和一个列表,栈存放运算符,列表存放操作数,栈和列表都为空。
  • 编译程序从左到右扫描原表达式,若遇到操作数,进入列表。
  • 若遇到运算符,与栈顶进行比较,比栈顶级别高则进栈,否则退出栈顶元素,将其加到列表中。
  • 若遇到左括号,进栈;若遇到右括号,则一直退栈输出到列表中,退到左括号为止。
  • 当栈变为空时,用迭代器将列表中的元素取出,每取出一个,都加一个空格作为分隔符,最后得到后缀表达式

迭代代码如下

 ListIterator li = list.listIterator();
 while (li.hasNext()) {
     Message += li.next() + " ";
     li.remove();
 }

四、测试方法

  • NifixToSuffix类的方法的测试
    20162311 结对编程项目-四则运算 阶段总结_第2张图片

  • Judgement类的方法测试

    • evaluate()方法
      20162311 结对编程项目-四则运算 阶段总结_第3张图片
    • isOperator()方法
      20162311 结对编程项目-四则运算 阶段总结_第4张图片

五、运行过程截图

20162311 结对编程项目-四则运算 阶段总结_第5张图片

六、代码托管地址

  • 地址链接

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

  • 问题1

如何计算表达式的值?

  • 解决方法

老师之前布置过一个课堂练习,让我们计算后缀表达式的值。老师把代码框架给了我们,教我们用栈进行计算。我将之前的那部分代码复制过来,新建一个Judgement类,把实现计算过程的代码作为判断的方法,通过获取返回值与用户输入的答案进行比较

package Arithmetic;

/**
 * Created by Administrator on 2017/5/13.
 */
import java.util.StringTokenizer;
import java.util.Stack;

public class Judgement
{
    /* 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 stack;

    public Judgement() {
        stack = new Stack();
    }

    public int evaluate (String expr)
    {
        int op1, op2, result = 0;
        String token;
        StringTokenizer tokenizer = new StringTokenizer (expr);

        while (tokenizer.hasMoreTokens())
        {
            token = tokenizer.nextToken();

            //如果是运算符,调用isOperator
            if (isOperator(token))
            {
                //从栈中弹出操作数2
                op2 = stack.pop();
                //从栈中弹出操作数1
                op1 = stack.pop();
                //根据运算符和两个操作数调用evalSingleOp计算result;
                result = evalSingleOp(token.toCharArray()[0], op1, op2);
                //计算result入栈;
                result = stack.push(result);
            }
            else //如果是操作数
                stack.push(Integer.parseInt(token));
            //操作数入栈;
        }

        return result;
    }

    public  boolean isOperator (String token)
    {
        return ( token.equals("+") || token.equals("-") ||
                token.equals("*") || token.equals("/") );
    }

    private int evalSingleOp (char operation, int op1, int op2)
    {
        int result = 0;

        switch (operation)
        {
            case ADD:
                result = op1 + op2;
                break;
            case SUBTRACT:
                result = op1 - op2;
                break;
            case MULTIPLY:
                result = op1 * op2;
                break;
            case DIVIDE:
                result = op1 / op2;
        }

        return result;
    }
}

  • 问题2

IntQuestions类生成的是中缀表达式,如何转化为后缀表达式呢?

  • 解决方法

也是运用栈。老师上课时也讲过,但是不清楚具体怎么实施。问了学的比较好的张旭升同学,看了一下他的代码。代码截图在上面有,具体操作在实现过程中的关键代码这点里面讲了,这里就不重复了。

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

  • 结对搭档:20162325金立清博客

  • 评价

与其说是结对编程,不如说是一个人编(搭档不给力)。坦白说,金立清同学之前学的不是很好,作为搭档,我也有责任,我没有起到监督作用。结对编程时基本上是我编,金立清同时也学习。金立清同学在经过我的讲解后,可以看懂代码,但是不会自己编。所以我觉得她最需要改进的就是把之前学的知识恶补一下,这样在以后的结对编程中才能有自己的想法,而不是只能看着我编,然后再讲解给她听。不过值得肯定的是她能认识到自己的不足,虚心学习,现在还来得及。

九、参考或引用的设计、实现

  • 后缀表达式求值

参考娄老师的mini dc课堂测试,这里是题目链接

 while (tokenizer.hasMoreTokens())
        {
            token = tokenizer.nextToken();

            //如果是运算符,调用isOperator
            if (isOperator(token))
            {
                //从栈中弹出操作数2
                op2 = stack.pop();
                //从栈中弹出操作数1
                op1 = stack.pop();
                //根据运算符和两个操作数调用evalSingleOp计算result;
                result = evalSingleOp(token.toCharArray()[0], op1, op2);
                //计算result入栈;
                result = stack.push(result);
            }
            else //如果是操作数
                stack.push(Integer.parseInt(token));
            //操作数入栈;
        }

这段代码老师只给了中文注释,是自己实现的,其余的框架是老师写好的

  • 中缀表达式转化成后缀表达式

参考娄老师的栈的应用PPT,学习了转化的思路;还请教了张旭升同学,并参考引用了他的代码

十、PSP

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

注:以上耗时均为本周耗时,下周完成后会有变动

本周的预计和实际耗时相差较大,主要是没有认识到任务的难度(高估了自己,哎),对Java的一些类的运用还不够熟练,所以在问同学,查资料上花了很多时间,不过这也促进了我的学习,希望下周能合理预估时间,同时学习到更多。

你可能感兴趣的:(20162311 结对编程项目-四则运算 阶段总结)