字符串表达式计算

可以进行加减乘除,指数运算,幂运算,阶乘运算,三角函数运算,对数函数运算。

由于前面几种比较好处理,所以在这里我主要阐述对于三角函数和对数函数的运算,可以采用递归的思想。

( 为了方便说明,本文直接使用一个例子来说明,下标都从0开始计数 )

首先,有个一个原始表达式,如 sin(1+2)*ln(2*3)+5。这个表达式很难用一次性全部计算出来,但可以把不含有函数的中缀表达式( 如sin(1+2)里面的1+2 ,sin(1+2)这样的表达式本文将此称为子表达式)转换为后缀表达式,然后再运算。

在这里我们定义四个栈,这四个栈分别是 运算表达式的表达式栈,子表达式的在其父表达式中的两个位置栈(对于sin(1+2),在其父表达式的第0个和第7个,两个栈分别存了0和7),子表达式的所代表的函数栈(对于sin(1+2),栈中存储的就是"sin")

        Stack<String> functionStack = new Stack<>();//存表达式
        Stack<Integer> leftStack = new Stack<>();//存子表达式左边
        Stack<Integer> rightStack = new Stack<>();//存子表达式右边
        Stack<String> numStack = new Stack<>();//存子表达式的函数
        String currentStr;//当前表达式,所有操作都是基于当前表达式

然后现在就有个问题,首先怎么定位子表达式,本文采用的是依此遍历,从左到右依次扫描字符串,如果遇到sin,cos,tan,ln这四种字符串就返回其在父字符串的位置,分别push进leftStack和rightStack中,再把父表达式push进functionStack中,再把子表达式的函数push进numStack中。并把当前字符串设置为不含有函数的中缀表达式( 如sin(1+2)里面的1+2,算出1+2后,把sin(3)替换父字符串(0.141*ln(2*3)+5),其他都是照葫芦画瓢了。

functionStack.push(currentStr);
int begin = functionBorder(currentStr);
int end = functionBorder(currentStr, begin);
leftStack.push(begin);
rightStack.push(end);
numStack.push(numOfFunction(currentStr));
currentStr = currentStr.substring(begin + numStack.peek().length() + 1, end);//得到这个函数里的字符串

因此,我们可以把sin(1+2)和ln(2*3)算出来,这样这个表达式就很好计算了,那就是0.141*1.098+5,这个式子就很简单了,这就是个标准只有加减乘除的表达式了。

还有人可能说,如果是函数嵌套(如:sin(cos(x)) )的话怎么算呢?这就是递归的魅力了,这就和递归树的后序遍历一样了。

    public static String calculate(String currentStr) {

        Stack<String> functionStack = new Stack<>();//存表达式
        Stack<Integer> leftStack = new Stack<>();//存子表达式左边
        Stack<Integer> rightStack = new Stack<>();//存子表达式右边
        Stack<String> numStack = new Stack<>();//存子表达式的函数

        while (!isNumber(currentStr) || !functionStack.isEmpty()) {
          
            if (containsSpecialStr(currentStr)) {//存在特殊函数
                functionStack.push(currentStr);
                int begin = functionBorder(currentStr);
                int end = functionBorder(currentStr, begin);
                leftStack.push(begin);
                rightStack.push(end);
                numStack.push(numOfFunction(currentStr));
                currentStr = currentStr.substring(begin + numStack.peek().length() + 1, end);//得到这个函数里的字符串
                //functionStack.push(currentStr);
            } else {//不存在特殊函数
                double calculate = Double.parseDouble(calculateWithoutStr(currentStr));//这个函数就是计算简单的中缀表达式的
                String suan = "";
                if (!numStack.isEmpty())//没有特殊函数的情况下
                    suan = numStack.pop();
                switch (suan) {
                    case "cos":
                        calculate = Math.cos(calculate);
                        break;
                    case "sin":
                        calculate = Math.sin(calculate);
                        break;
                    case "tan":
                        calculate = Math.tan(calculate);
                        break;
                    case "ln":
                        calculate = Math.log(calculate);
                        break;
                }
                if (!functionStack.isEmpty()) {
                    currentStr = new StringBuilder(functionStack.pop()).replace(leftStack.pop(),
                            rightStack.pop() + 1, String.valueOf(calculate)).toString();
                } else
                    currentStr = String.valueOf(calculate);
            }
        }
        return currentStr;
}
//正则表达式 确认表达式是否为浮点数
    public static boolean isNumber(String str) {
        String reg = "^[-+]?[0-9]+(.[0-9]+)?$";
        return str.matches(reg);
    }

    private static boolean containsSpecialStr(String str) {
        return str.contains("sin") || str.contains("cos") || str.contains("ln") || str.contains("tan");
    }

    private static int functionBorder(String expression) {
        char[] chars = expression.toCharArray();
        for (int j = 0; j < chars.length; j++) {//+ - * / ^ ! ( )
            if (!(isDigit(chars[j]) || chars[j] == '+' || chars[j] == '-' || chars[j] == '*' || chars[j] == '/' ||
                    chars[j] == '!' || chars[j] == '^' || chars[j] == '(' || chars[j] == ')' || chars[j] == '.')) {
                return j;//如果是字母就返回,得到起始字符
            }
        }
        return -1;//表示无字母
    }

    private static String numOfFunction(String expression) {
        char[] chars = expression.toCharArray();
        for (char aChar : chars) {
            if (aChar == 'c')//如果是字母就返回,得到起始字符
                return "cos";
            else if (aChar == 's')
                return "sin";
            else if (aChar == 't')
                return "tan";
            else if (aChar == 'l')
                return "ln";
        }
        return "";//表示无字母
    }

    private static int functionBorder(String expression, int begin) {//从0开始
        int leftParenthesis = 1;
        char current;
        int end = begin;
        boolean flag = true;
        while (leftParenthesis != 0 && end + 1 != expression.length()) {
            end = end + 1;
            current = expression.charAt(end);
            if (current == '(') {
                leftParenthesis++;
                if (flag) {
                    leftParenthesis--;
                    flag = false;
                }
            } else if (current == ')')
                leftParenthesis--;
        }
        if (leftParenthesis != 0)
            throw new RuntimeException("表达式出错,括号少了");
        return end;
    }

这个函数里除了计算简单中缀表达式的函数,其他都给了

double calculate = Double.parseDouble(calculateWithoutStr(currentStr));

就只有calculateWithoutStr()这个函数没给,这网上随随便便都找得到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值