• 20165310java_teamExp1_week1


    结对编程项目-四则运算-week1

    需求分析

    • 第一周达成
      • 支持真分数的四则运算
      • 支持多运算符
      • 能手动输入n道题目,n由使用者输入
    • 后续拓展的可能
      • 能随机生成n道题目,n由使用者输入
      • 能够判断正误,错误时能提醒并输出正确答案
      • 能计算出正确率
      • 能多次生成题目,直到使用者选择退出
      • 题目去重
      • 处理生成题目并输出到文件
      • 完成题目后从文件读入并判题
      • 多语言支持:简体中文, 繁體中文, English

    设计思路

    • 将带括号的四则运算式的字符串转化为逆波兰式,具体思路在[2016-2017-2 《Java 程序设计》课堂实践项目]中有明确的说明:

    1.设立一个栈,存放运算符,首先栈为空;
    2.从左到右扫描中缀式,若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符;
    3.若遇到运算符,则与栈顶比较,比栈顶级别高则进栈,否则退出栈顶元素并输出,然后输出一个空格作分隔符;
    4.若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。
    5.当栈变成空时,输出的结果即为后缀表达式。
    根据上述思路,完成代码ChangeExpress.java,将前缀式改为后缀式,并且完成分析括号匹配的功能,若左右括号不匹配,输出错误并退出程序运行:

    import java.util.*;
    public class ChangeExpress{
        String originalExpression;
        String changedExpression= "";
        int countLeft=0,countRight=0;
        public void setOriginalExpression(String str){
            originalExpression=str;
        }
        public void changedWay(){
            Stack stackChange=new Stack();//创立栈
            int opValue []=new int[100];
            for (int i=0;i<originalExpression.length() ;i++) {
                char chi=originalExpression.charAt(i);
                if (chi>='0'&&chi<='9'){
                    changedExpression=changedExpression+chi;
                }
                else if (chi=='+'||chi=='-'||chi=='*'||chi=='÷'||chi=='/') {
                    changedExpression=changedExpression+" ";//有运算符,数字之间就要有空格,否则是一个整体
                    if (stackChange.empty()){//栈为空直接压栈
                        stackChange.push(chi);
                    }
                    else if (judgeValue(chi)>=judgeValue((char)stackChange.peek())) {//运算级别高或者相等压入栈
                        stackChange.push(chi);
                    }
                    else{
                        changedExpression=changedExpression+ String.valueOf(stackChange.pop())+" ";//否则直接进入字符串,空格分割运算符
                        i--;
                    }
                }
                else if(chi=='('){
                    countLeft++;
                    stackChange.push(chi);//左括号压栈
                }
                else if(chi==')'){
                    changedExpression+=" ";
                    countRight++;
                    while((char)stackChange.peek()!='('){//直到(为止
                        changedExpression=changedExpression+ String.valueOf(stackChange.pop())+" ";//弹出栈内东西,空格分割
                    }
                    stackChange.pop();
                }
            }
            changedExpression+=" ";
            while(!stackChange.empty()){
                changedExpression=changedExpression+String.valueOf(stackChange.pop())+" ";
            }
        /*int size = stackChange.size();
        for (int i=0;i<size;i++){
            changedExpression=changedExpression+String.valueOf(stackChange.pop())+" ";
        }*/
            if (countLeft!=countRight) {
                System.out.println("括号不匹配");
                System.exit(0);
            }
        }
        public int judgeValue(char c){
            int value=0;
            switch(c){
                case '(':
                    value=1;
                    break;
                case '+':
                case '-':
                    value=2;
                    break;
                case '*':
                case '÷':
                    value=3;
                    break;
                case '/':
                    value=4;
                    break;
                case ')':
                    value=5;
                default:
                    value=0;
            }
            return value;
        }
    }
    
    
    • 进行后缀式计算:学习了[2016-2017-2 《Java 程序设计》课堂实践项目]之后,发现老师的参考代码MyDC.java,原理是:利用空格作为分隔符将后缀式表达的字符串进行分割,遇到操作数就压栈,遇到操作符就弹出栈顶的两位操作数进行运算,再将运行结果压栈,直到没有下一个分割好的字符串,输出结果:
    import java.util.StringTokenizer;
    import java.util.Stack;
    
    public class MyDcRational
    {
        /** 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 DIVIDE1 = '÷';
        private  final char DIVIDE2='/';
        /** the stack */
        private Stack stack;//存放操作数的栈
        public MyDcRational()
        {
            stack = new Stack();
        }
    
        public Rational evaluate (String expr)
        {
            Rational op1=new Rational();
            Rational op2=new Rational();
            Rational result=new Rational();
            result.setNumerator(0);
            String token;
            StringTokenizer tokenizer = new StringTokenizer (expr);//划分表达式
    
            while (tokenizer.hasMoreTokens())
            {
                token = tokenizer.nextToken();//将算数表达式分解的
    
                if (isOperator(token))//见下方isOperateor方法,当是运算符的时候进入if语句
                {
                    op2 = (Rational) stack.pop();
                    op1 = (Rational)stack.pop();//弹出最上面两个操作数
                    result = evalSingleOp (token.charAt(0), op1, op2);//见下方evaSingleOp方法
                    stack.push (result);//将计算结果压栈
                }
                else{
                    Rational num=new Rational();
                    num.setNumerator(Integer.parseInt(token));//将操作数由string转变为Rational
                    stack.push (num);//操作数入栈
                }
    
            }
    
            return result;//输出结果
        }
    
        private boolean isOperator (String token)//判断是否为运算符,注意用equal语句比较字符串
        {
            return ( token.equals("+") || token.equals("-") ||
                    token.equals("*") || token.equals("÷")||token.equals("/"));
        }
    
        private Rational evalSingleOp (char operation, Rational op1, Rational op2)
        {
            Rational result=new Rational();
            result.setNumerator(0);
            switch (operation)
            {
                case ADD:
                    result = op1.add(op2);
                    break;
                case SUBTRACT:
                    result = op1.sub(op2);
                    break;
                case MULTIPLY:
                    result = op1.muti(op2);
                    break;
                case DIVIDE1:
                case DIVIDE2:
                    result = op1.div(op2);
                    break;
                default:
                    System.out.println("Error!");
            }
            return result;
        }
    }
    
    
    
    • 最后编写主函数代码Calculation.java,实现功能有:运算式输入、运算、结果输出:
    import java.util.*;
    
    public class Calculation {
        public static void main(String[] args) {
            System.out.println("请输入要生成的题目数:");
            Scanner number = new Scanner(System.in);
            int n = number.nextInt();
            for (int i = 1; i <= n; i++) {
                System.out.println("题目" + i + ":");
                Scanner reader = new Scanner(System.in);
                Rational result = new Rational();
                String str = reader.nextLine();
                ChangeExpress change = new ChangeExpress();
                change.setOriginalExpression(str);
                //System.out.println(change.originalExpression);
                change.changedWay();//后缀式化
                //System.out.println(change.changedExpression);
                MyDcRational calculate = new MyDcRational();//后缀式计算
                result = calculate.evaluate(change.changedExpression);
                int a = result.getNumerator();
                int b = result.getDenominator();
                if (b == 1) {
                    System.out.println("result=" + a);
                } else {
                    System.out.println("result=" + a + "/" + b);
                }
    
            }
    
        }
    }
    

    UML截图

    Q20180415-

    功能截图

    示例结果:
    幕快照 2018-04-16 上午12.00.0

    运行结果:
    E4FC262DE2E01E1B0DD39784B4D51E

    重要代码注释

    关键代码在设计思路模块已经解释了整体思路,并给出了具体代码,并且代码中给了注释。

    测试截图

    由于我们代码多为void类型,能够进行测试的代码并不多,下面为成功的测试截图

    • 判断符号优先级
      幕快照 2018-04-16 下午6.48.2
    • 测试分数运算
      幕快照 2018-04-16 下午6.47.4

    遇到的困难及解决方法

    • 由于上周的实验我正好做的是四则运算,相对其他组更加理解一些关于栈的原理,但是在上周的实验中,我没有将'÷'与'/'分开,统一将分数与除法都当做除法运算进行处理压栈,但是本周实验中,这两者被区分开,开始我只是在switch-case语句判断运算符优先级的时候将'÷'与'/'设置为一样,依旧试图将'÷'与'/'等价,但是我的小伙伴阎含在测试中发现,如下的式子将会报错:
      1/2÷5/8,如果按照我的思路,后缀式将是‘1 2 / 5 ÷ 8 /’,但实际的后缀式应该是:‘1 2 / 5 8 / ÷’,因此我重新定义了运算符优先级'/'比'÷'高一级,运算结果正确
    • 由于上周的实验正好做的是四则运算是优势也是弊端,我们整体的设计要延续上周的思路进行设计,要进行思路的整理回忆和改编,所以我们这种更多进行了逻辑框架上的思考与搭建,例如如何设计语言类调用三种语言而不违背S.O.L.I.D原则中的SRP原则,如何生成带有括号的随机四则运算,如何进行去重,更多的具体实现将在下周进行。

    对结对的小伙伴做出评价

    我的结对小伙伴阎寒很细致耐心,我们可以进行深入探讨,在意见分歧或者时间紧促的时候的时候也能不骄不躁,是个很好的搭档。

    PSP

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

    代码托管地址

    https://gitee.com/BESTI-IS-JAVA-2018/20165310exp/tree/master/expTeamWeek1

  • 相关阅读:
    hdu 3791 二叉搜索树
    hdu 4041 Eliminate Witches! 栈和队列
    后缀数组讲解
    Theme Section HDU
    Wow! Such Doge! HDU
    Girls' research HDU
    吉哥系列故事――完美队形II HDU
    Best Reward HDU
    String Problem HDU
    最大最小表示法
  • 原文地址:https://www.cnblogs.com/atbaoi/p/8850316.html
Copyright © 2020-2023  润新知