• 双人结对,四则运算(三阶段)


    1、第一阶段目标:重构四则运算-- 能把计算的功能封装起来,通过API 接口调用计算方法。定义一个计算核心类:把四则运算的计算功能包装在一个模块中 (这个模块可以是一个类 Class,  一个DLL等等)。“计算核心”模块和调用类它的其他模块之间是什么关系呢? 它们要通过一定的API (Application Programming Interface) 来和其他模块交流。这个API 接口应该怎么设计呢? 可以从下面的最简单的接口开始:Calc()这个Calc 函数接受字符串的输入(字符串里就是运算式子,例如 “ 5+3.5“,  “7/8 – 3/8 ”,  “3 + 90 * (-0.3)“  等等

    2 第二阶段目标 - 通过测试程序和API 接口测试其简单的加减乘除功能。并能看到代码覆盖率。可以扩展 Calc() 的定义,让它接受一个新的参数 “precision”,  或者可以启用一个新的函数 Setting()。最多4 个运算符数值范围是 -1000 到 1000精度是小数点后两位怎么通过API 告诉我们的模块呢?  我们当然可以用函数的参数直接传递,但是参数的组合很多,怎么定义好参数的规范呢?   建议大家考虑用 XML 来传递这些参数。增加了新的Setting() 函数之后,要让模块支持这样的参数,同时,还要保证原来的各个测试用例继续正确地工作。

    3第三阶段目标 – 定义异常处理。

    如果输入是有错误的,例如 “1 ++ 2”, 在数值范围是 -1000 .. 1000 的时候,传进去 “10000 + 32768 * 3”,  或者是 “ 248.04 / 0”  怎么办? 怎么告诉函数的调用者 “你错了”?  把返回的字符串定义为 “-1” 来表示? 那么如果真的计算结果是 “-1” 又怎么处理呢? 建议这个时候,要定义各种异常 (Exception), 让 Core 在碰到各种异常情况的时候,能告诉调用者 - 你错了! 当然,这个时候,同样要进行下面的增量修改:定义要增加什么功能 - 例如:支持 “运算式子格式错误” 异常,写好测试用例,传进去一个错误的式子,期望能捕获这个 异常。 如果没有,那测试就报错。在 Core 模块中实现这个功能,测试这个功能, 同时测试所有以前的功能,保证以前的功能还能继续工作 (没有 regression), 确认功能完成,继续下一个功能。),这个模块的返回值是一个字符串,例如,前面几个例子的结果就是 ( ”17.5“, “ 1/2”, “-24“).

    package com.core.bean;
    
    import java.math.BigDecimal;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Stack;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import com.core.util.JaxbUtil;
    
    public class Calculate {
        
        private static final Pattern EXPRESSION_PATTERN = Pattern.compile("[0-9\.+-/*()= ]+");
    
        private static final Map<String,Integer> OP_PRIORITY_MAP = new HashMap<String ,Integer>() {
            /**
             * 
             */
            private static final long serialVersionUID = -5028404412583819744L;
    
            {
                put("(",0);
                put("+",2);
                put("-",2);
                put("*",3);
                put("/",3);
                put(")",7);
                put("=",20);
            }
        };
        
        //不带xml参数的计算
        public static String Calc(String expression) throws MyException {
            
            //判断表达式是否为空
            if(expression==null||expression.equals("")) {
                throw new IllegalArgumentException("表达式不能为空!");
            }
            
            Matcher matcher = EXPRESSION_PATTERN.matcher(expression);
            if(!matcher.matches()){
                throw new IllegalArgumentException("表达式含有非法字符!");
            }
            
            expression = formatExpression(expression);
            isExpression(expression);
            
            Stack<String> optstack = new Stack<>();
            Stack<BigDecimal> numStack = new Stack<>();
            StringBuilder numBuilder = new StringBuilder(16);
            char pre;
            
            
            
            for(int i=0;i<expression.length();i++) {
                char ch = expression.charAt(i);
                
                if(ch!=' ') {
                    pre = ch;
                    
                    if((ch>='0'&&ch<='9')||ch=='.') {
                        numBuilder.append(ch);
                    }else {
                        if(numBuilder.length()>0) {
                            if(isNum(numBuilder.toString())==false) {
                                throw new MyException("表达式格式错误!————数字格式错误(错误数字:'"+numBuilder+"'");
                            }
                            numStack.push(new BigDecimal(numBuilder.toString()));
                            numBuilder.delete(0, numBuilder.length());
                        }
                        String operation = String.valueOf(ch);
                        if(optstack.empty()) {
                            optstack.push(operation);
                        }else {
                            if(operation.equals("(")) {
                                optstack.push(operation);
                            }else if(operation.equals(")")) {
                                directCal(optstack, numStack,true);
                            }else if(operation.equals("=")) {
                                directCal(optstack, numStack,false);
                                return numStack.pop().toString();
                            }else {
                                compareOpt_Cal(numStack, optstack, operation);
                            }
                        }
                    }
                }
            }
            
            if(numBuilder.length()>0) {
                numStack.push(new BigDecimal(numBuilder.toString()));
            }
            directCal(optstack, numStack, false);
            return numStack.pop().toString();
            
            
        }
        
        //带xml参数的计算
        public static String Calc(String expression,String xml) throws MyException {
            
            //判断表达式是否为空
            if(expression==null||expression.equals("")) {
                throw new IllegalArgumentException("表达式不能为空!");
            }
            
            Matcher matcher = EXPRESSION_PATTERN.matcher(expression);
            if(!matcher.matches()){
                throw new IllegalArgumentException("表达式含有非法字符!");
            }
            
            expression = formatExpression(expression);
            isExpression(expression);
            
            Option option=JaxbUtil.convertToJavaBean(xml, Option.class);
            int op_num = 0;
            
            Stack<String> optstack = new Stack<>();
            Stack<BigDecimal> numStack = new Stack<>();
            StringBuilder numBuilder = new StringBuilder(16);
            
            for(int i=0;i<expression.length();i++) {
                char ch = expression.charAt(i);
                if(ch!=' ') {
                    if((ch>='0'&&ch<='9')||ch=='.') {
                        numBuilder.append(ch);
                    }else {
                        if(++op_num>option.getOperation_num())
                            throw new MyException("操作符个数超过设置最大个数!");
                        
                        if(numBuilder.length()>0) {
                            BigDecimal value = new BigDecimal(numBuilder.toString());
                            if(value.doubleValue()>option.getMax()||value.doubleValue()<option.getMin())
                                throw new MyException("数据超出设置范围!");
                            if(isNum(numBuilder.toString())==false) {
                                throw new MyException("表达式格式错误!————数字格式错误(错误数字:'"+numBuilder+"'");
                            }
                            numStack.push(value);
                            numBuilder.delete(0, numBuilder.length());
                        }
                        String operation = String.valueOf(ch);
                        if(optstack.empty()) {
                            optstack.push(operation);
                        }else {
                            if(operation.equals("(")) {
                                optstack.push(operation);
                            }else if(operation.equals(")")) {
                                directCal(optstack, numStack,true);
                            }else if(operation.equals("=")) {
                                directCal(optstack, numStack,false);
                                return String.format("%."+option.getPoint_long()+"f", numStack.pop());
                            }else {
                                compareOpt_Cal(numStack, optstack, operation);
                            }
                        }
                    }
                }
            }
            
            if(numBuilder.length()>0) {
                numStack.push(new BigDecimal(numBuilder.toString()));
            }
            directCal(optstack, numStack, false);
            return String.format("%."+option.getPoint_long()+"f", numStack.pop());
            
            
        }
        
        
        private static void compareOpt_Cal(Stack<BigDecimal> numstack,Stack<String> optstack,String operation) throws MyException {
            String topOpt = optstack.peek();
            int comp_val = getPriority(topOpt, operation);
            if(comp_val==-1||comp_val==0) {
                String opt = optstack.pop();
                BigDecimal num2 = numstack.pop();
                BigDecimal num1 = numstack.pop();
                BigDecimal result = calDouble(opt, num1, num2);
                numstack.push(result);
                if(optstack.empty()) {
                    optstack.push(operation);
                }else {
                    compareOpt_Cal(numstack, optstack, operation);
                }
            }else {
                optstack.push(operation);
            }
        }
        
        //判断两个操作符的优先级
        private static int getPriority(String op1,String op2) {
            return OP_PRIORITY_MAP.get(op2)-OP_PRIORITY_MAP.get(op1);
        }
        
        //两个数之间的运算,注意除数不能为0
        private static BigDecimal calDouble(String operation,BigDecimal num1,BigDecimal num2) throws MyException {
            BigDecimal result = new BigDecimal(0);
            switch (operation) {
            case "+":
                result = num1.add(num2);
                break;
            case "-":
                result = num1.subtract(num2);
                break;
            case "*":
                result = num1.multiply(num2);
                break;
            case "/":
                if(num2.equals(BigDecimal.ZERO)) {
                    throw new MyException("表达式格式错误!————数字格式错误(除数不能为0:'"+num1+"/"+num2+"')");
                }
                result = num1.divide(num2,10,BigDecimal.ROUND_DOWN);
                break;
            default:
                break;
            }
            return result;
        }
        
        //递归计算
        private static void directCal(Stack<String> opStack,Stack<BigDecimal> numStack,boolean haved) throws MyException {
            String opt = opStack.pop();
            BigDecimal num2 = numStack.pop();
            BigDecimal num1 = numStack.pop();
            BigDecimal result = calDouble(opt, num1, num2);
            
            numStack.push(result);
            
            if(haved) {
                if("(".equals(opStack.peek())) {
                    opStack.pop();
                }else {
                    directCal(opStack, numStack,haved);
                }
            }else {
                if(!opStack.empty()) {
                    directCal(opStack, numStack, haved);
                }
            }
        }
        
        //判断表达式的合法性(字符合法性)
        public static void isExpression(String expression) throws MyException {
            char pre=' ',cur=' ';
            int l_bracket_num=0,r_bracket_num=0;
            for(int i=0;i<expression.length();i++) {
                cur=expression.charAt(i);
                if(cur=='(') {
                    l_bracket_num++;
                }else if(cur==')') {
                    r_bracket_num++;
                }
                if(i>0) {
                    if(pre=='.'&&(cur=='.'||cur=='('||cur==')'||cur=='+'||cur=='-'||cur=='*'||cur=='/')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((pre=='.'||pre=='('||pre==')'||pre=='+'||pre=='-'||pre=='*'||pre=='/')&&cur=='.') {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((cur=='.'||cur==')'||cur=='+'||cur=='-'||cur=='*'||cur=='/')&&pre=='(') {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((pre=='.'||pre=='('||pre=='+'||pre=='-'||pre=='*'||pre=='/')&&cur==')') {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if((pre=='+'||pre=='-'||pre=='*'||pre=='/')&&(cur=='+'||cur=='-'||cur=='*'||cur=='/')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(cur=='='&&i!=expression.length()-1) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(cur=='='&&(pre=='+'||pre=='-'||pre=='*'||pre=='/'||pre=='(')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(cur=='('&&(pre>='0'&&pre<='9')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }else if(pre==')'&&(cur>='0'&&cur<='9')) {
                        throw new MyException("表达式格式错误!————操作符格式错误(错误字符区域:'"+pre+cur+"')");
                    }
                }else {
                    if(cur=='/'||cur=='*'||cur=='.'||cur==')'||cur=='=')
                        throw new MyException("表达式格式错误!————表达式首字符不符合规范(错误字符:'"+cur+"'");
                }
                
                pre=cur;
            }
            if(l_bracket_num!=r_bracket_num)
                throw new MyException("表达式格式不正确!————存在不匹配的左右括号");
        }
        
        //判断表达式中数字的合法性
        public static boolean isNum(String num) {
            if(num.contains(".")) {
                for(int i=0;i<num.indexOf('.');i++){
                    if(i==0&&num.charAt(0)=='0'&&num.indexOf('.')>1) {
                        return false;
                    }
                }
            }else {
                if(num.charAt(0)=='0'&&num.length()>1)
                    return false;
            }
            return true;
        }
        
        //判断表达式的开头是否为+或-,如果是,则在其前面加上0方便操作
        public static String formatExpression(String expression) {
            String result="";
            if(expression.charAt(0)=='+'||expression.charAt(0)=='-')
                expression = "0"+expression;
            result = expression.replaceAll("[(]-", "(0-");
            result = result.replaceAll("[(][+]", "(0+");
            return result;
        }
        
        public static void main(String[] args) throws MyException {
            Option option = new Option();
            //设置最大字符数
            option.setOperation_num(10);
            //设置数值范围的最小值
            option.setMin(-1000);
            //设置数值范围的最大值
            option.setMax(1000);
            //设置结果保留的小数位
            option.setPoint_long(2);
            
            String xml = JaxbUtil.convertTomXml(option);
            
            //传入带xml参数的计算方法(如不需要直接去掉该参数即可)
            System.out.println(Calc("12+87",xml));
        }
    }
    package com.core.bean;
    
    public class MyException extends Exception{
        /**
         * 
         */
        private static final long serialVersionUID = 4709550237708038191L;
        public MyException() {}
        public MyException(String message) {super(message);}
    }
    package com.core.bean;
    
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    public class Option {
        private int operation_num;
        private double min;
        private double max;
        private int point_long;
        public int getOperation_num() {
            return operation_num;
        }
        public void setOperation_num(int operation_num) {
            this.operation_num = operation_num;
        }
        public double getMin() {
            return min;
        }
        public void setMin(double min) {
            this.min = min;
        }
        public double getMax() {
            return max;
        }
        public void setMax(double max) {
            this.max = max;
        }
        public int getPoint_long() {
            return point_long;
        }
        public void setPoint_long(int point_long) {
            this.point_long = point_long;
        }
        
    }
    package com.core.util;
    
    import java.io.StringReader;
    import java.io.StringWriter;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    
    import com.core.bean.Option;
    
    public class JaxbUtil {
        
        //javabean转换成xml
        private static String converToXml(Object obj,String encoding) {
            String result = null;
            try {
                JAXBContext context = JAXBContext.newInstance(obj.getClass());
                Marshaller marshaller = context.createMarshaller();
                //是否格式化xml(按标签自动换行)
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                //设置编码方式
                marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
                StringWriter writer = new StringWriter();
                marshaller.marshal(obj, writer);
                result = writer.toString();
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            return result;
        }
        
        public static String convertTomXml(Object object) {
            return converToXml(object, "UTF-8");
        }
        
        //xml转换成javabean
        @SuppressWarnings("unchecked")
        public static<T> T convertToJavaBean(String xml,Class<T> c) {
            T t = null;
            try {
                JAXBContext context = JAXBContext.newInstance(c);
                Unmarshaller unmarshaller = context.createUnmarshaller();
                t = (T)unmarshaller.unmarshal(new StringReader(xml));
            }catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
            
            return t;
        }
        
        public static void main(String[] args) {
            Option option = new Option();
            option.setOperation_num(4);
            option.setMin(-1000);
            option.setMax(1000);
            option.setPoint_long(2);
    //        System.out.println(convertTomXml(option));
            String xml = convertTomXml(option);
            
            Option option2=convertToJavaBean(xml, Option.class);
            System.out.println(option2.getMin()+"~"+option2.getMax());
        }
    }
  • 相关阅读:
    模板 无源汇上下界可行流 loj115
    ICPC2018JiaozuoE Resistors in Parallel 高精度 数论
    hdu 2255 奔小康赚大钱 最佳匹配 KM算法
    ICPC2018Beijing 现场赛D Frog and Portal 构造
    codeforce 1175E Minimal Segment Cover ST表 倍增思想
    ICPC2018Jiaozuo 现场赛H Can You Solve the Harder Problem? 后缀数组 树上差分 ST表 口胡题解
    luogu P1966 火柴排队 树状数组 逆序对 离散化
    luogu P1970 花匠 贪心
    luogu P1967 货车运输 最大生成树 倍增LCA
    luogu P1315 观光公交 贪心
  • 原文地址:https://www.cnblogs.com/yangqqq/p/13070908.html
Copyright © 2020-2023  润新知