IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java递归调用—实现自定义四则运算表达式解析/计算 -> 正文阅读

[Java知识库]Java递归调用—实现自定义四则运算表达式解析/计算

递归调用

先拷贝一下百科上递归调用的定义啊:

  • 递归调用是一种特殊的嵌套调用,是某个函数调用自己或者是调用其他函数后再次调用自己的,只要函数之间互相调用能产生循环的则一定是递归调用,递归调用一种解决方案,一种是逻辑思想,将一个大工作分为逐渐减小的小工作。
  • 就比如一个人吃饭,我们不知道饭到底有多少,需要多少口能够吃完。我们只定义一个“吃一口”的方法,每次执行“吃一口”之后,就再次调用这个方法,直到饭变成空的了。

递归的思想,一个大型的复杂的问题,层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程。递归过程中,一定有一个结束条件,当满足这个条件之后,程序就返回,不再继续递归调用。

解析一个四则运算表达式

一个自定义的四则运算公式:

(-${a} + (${b} + ${c} * ${d}) - 3 * 7 * ${e}) * ${f}

其中 a ~ f,都是变量,通过 “${}” 包裹,给出 a ~ f 的值以后,计算这个公式的结果。可以将这个表达式,解析为每一步运算的过程,即每次都是两个变量进行操作,最后得出结果。
按照四则运算的规则,先对括号进行解析,当解析为不包含括号的表达式后,再按照先乘除,后加减解析的规则,进行拆分,直到解析为两个变量的操作,得到下面这样的解析结果:

(-${a} + (${b} + ${c} * ${d}) - 3 * 7 * ${e}) * ${f}

var0 = ${c}*${d}
var1 = ${b}+${var0}
var2 = 3*7
var3 = ${var2}*${e}
var4 = 0-${a}
var5 = ${var4}+${var1}
var6 = ${var5}-${var3}
var7 = ${var6}*${f}

每一步的结果,用一个变量保存,下面的表达式会引用该变量,最后一个表达式的结果,就是最终结果。

代码实现

  • parseFormula:解析表达式,解析括号部分。
    如果表达式中没有括号了,则调用解析四则运算的方法。如果有括号,将括号中的字符串,再次调用本身方法。最后括号部分用变量替换,再次调用本身方法进行处理。
  • parseFourArithmetic:解析四则运算。
    如果表达式中只包含同一类运算符号,即只有加减或者乘除,则调用解析简单表达式的方法。如果加减和乘除都存在,将乘除部分取出,再次调用本身方法。最后乘除部分用变量替换,再次调用本身方法进行处理。
  • parseSimpleFormula:解析简单表达式。
    此时的输入的表达式,只包含加减,或者乘除了,只需从左往右进行解析。如果只包含一个操作符号了,则返回;否则将前面两个变量替换后,再次调用本身方法处理。

最终,通过上面三个方法的递归调用,将表达式解析为n条只包含一个运算符号的表达式。
计算时,循环遍历解析结果,进行变量的替换,将每一步的结果都放入变量的map中,后续的计算会用到。最后一个表达式的结果,就是最终的结果了。
PS:解析出的List<Formula>表达式集合,可以根据表达式字符串,缓存下来,下一次就不需要解析了,直接根据字符串获取缓存的表达式集合即可


import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.*;

/**
 * @Description 四则运算公式工具类
 */
@Slf4j
public class FormulaUtil {

    public static void main(String[] args) {

        String str = "(-${a} + (${b} + ${c} * ${d}) - 3*7*${e}) * ${f}";
        List<Formula> strings = parseFormula(str);
        for (Formula string : strings) {
            System.out.println(string);
        }

        Map<String, Number> varMap = new HashMap<>();
        varMap.put("a", 1);
        varMap.put("b", 2);
        varMap.put("c", 3);
        varMap.put("d", -5);
        varMap.put("e", 5);
        varMap.put("f", 10);

        double calculate = calculate(strings, varMap);
        System.out.println(calculate);
    }

    /**
     * 解析并计算表达式
     * @param str 表达式
     * @param varMap 变量map
     * @return 结果
     */
    public static double calculate(String str, Map<String, Number> varMap) {
        return calculate(parseFormula(str), varMap);
    }

    /**
     * 计算表达式
     * @param formulaList 步骤表达式
     * @param varMap 变量map
     * @return 结果
     */
    public static double calculate(List<Formula> formulaList, Map<String, Number> varMap) {
        if (formulaList == null || formulaList.isEmpty()) {
            throw new IllegalArgumentException("计算流程不能为空 formulaList is null or empty");
        }

        Number res = null;

        for (Formula formula : formulaList) {
            res = formula.calculate(varMap);
            varMap.put(formula.getVariable(), res);
        }

        return res.doubleValue();
    }

    /**
     * 解析表达式
     * @param formula 表达式
     * @return 结果
     */
    public static List<Formula> parseFormula(String formula) {
        formula = formula.replaceAll(" ", "");
        int length = formula.length();
        int leftBracketSum = 0;
        int rightBracketSum = 0;
        for (int i = 0; i < length; i++) {
            char c = formula.charAt(i);
            if (c == '(') {
                leftBracketSum++;
            }
            if (c == ')') {
                rightBracketSum++;
            }
        }

        if (leftBracketSum != rightBracketSum) {
            throw new IllegalArgumentException("公式异常, 左括号和右括号数量不相等");
        }
        ArrayList<Formula> formulaList = new ArrayList<>();

        if (leftBracketSum == 0) {
            formulaList.addAll(parseFourArithmetic(formula));
            return formulaList;
        }

        int firstBraces = formula.indexOf("(");
        if (firstBraces > -1) {
            int firstBracesOther = getMirrorIndex(formula, firstBraces);
            // 括号里面的表达式
            String subFormula = formula.substring(firstBraces + 1, firstBracesOther);
            List<Formula> sfs = parseFormula(subFormula);
            formulaList.addAll(sfs);

            String fs = new StringBuilder()
                    .append(formula.substring(0, firstBraces))
                    .append(sfs.get(sfs.size() - 1).getVariableStr())
                    .append(formula.substring(firstBracesOther + 1))
                    .toString();
            formulaList.addAll(parseFormula(fs));
        }

        return formulaList;
    }

    /**
     * 解析只包含四则运算的表达式,不包含括号
     * @param formula 表达式
     * @return 结果
     */
    private static List<Formula> parseFourArithmetic(String formula) {
        if (formula.startsWith("-") || formula.startsWith("+")) {
            formula = "0" + formula;
        }

        String[] addSub = new String[]{"+", "-"};
        String[] mutDiv = new String[]{"*", "/"};

        int addSubCount = countChars(formula, addSub);
        int mutDivCount = countChars(formula, mutDiv);
        if (addSubCount == 0 && mutDivCount == 0) {
            throw new IllegalArgumentException("公式错误: " + formula);
        }

        List<Formula> formulaList = new ArrayList<>();

        if ((addSubCount + mutDivCount) == 1) {
            formulaList.add(new Formula(formula));
            return formulaList;
        }

        if (mutDivCount == 0) {
            // 仅有加减算术符号
            formulaList.addAll(parseSimpleFormula(formula, addSub));
            return formulaList;
        } else if (addSubCount == 0) {
            // 仅有乘除算术符号
            formulaList.addAll(parseSimpleFormula(formula, mutDiv));
            return formulaList;
        }

        int maxIndex = formula.length();

        int startIndex = findFirstIndex(formula, 0, maxIndex, mutDiv);

        int nextAddSub = findFirstIndex(formula, startIndex, maxIndex, addSub);
        int preAddSub = findLastIndex(formula, 0, startIndex, addSub);

        preAddSub = preAddSub < 0 ? 0 : preAddSub + 1;
        nextAddSub = nextAddSub < 0 ? maxIndex : nextAddSub;

        String subFormula = formula.substring(preAddSub, nextAddSub);

        List<Formula> subFormulas = parseFourArithmetic(subFormula);
        formulaList.addAll(subFormulas);

        String fs = new StringBuilder()
                .append(formula.substring(0, preAddSub))
                .append(subFormulas.get(subFormulas.size() - 1).getVariableStr())
                .append(formula.substring(nextAddSub))
                .toString();
        formulaList.addAll(parseFourArithmetic(fs));
        return formulaList;
    }

    /**
     * 解析只包含同一类运算符的表达式,只包含加减,或者乘除
     * @param formula 表达式
     * @param typeChars 加减,或者乘除 的运算符号
     * @return 最终结果:仅有两个变量的操作表达式
     */
    private static List<Formula> parseSimpleFormula(String formula, String... typeChars) {
        List<Formula> formulaList = new ArrayList<>();
        int maxIndex = formula.length();

        int firstIndex = findFirstIndex(formula, 0, maxIndex, typeChars);
        int secondIndex = findFirstIndex(formula, firstIndex + 1, maxIndex, typeChars);

        if (secondIndex == -1) {
            Formula ff = new Formula(formula);
            formulaList.add(ff);
            return formulaList;
        }

        String firstFormula = formula.substring(0, secondIndex);
        Formula ff = new Formula(firstFormula);
        formulaList.add(ff);

        String fs = new StringBuilder()
                .append(ff.getVariableStr())
                .append(formula.substring(secondIndex))
                .toString();

        formulaList.addAll(parseSimpleFormula(fs, typeChars));
        return formulaList;
    }

    private static int countChars(String formula, String... chars) {
        int count = 0;
        for (int i = 0; i < formula.length(); i++) {
            for (String aChar : chars) {
                if (aChar.equals(formula.charAt(i) + "")) {
                    count++;
                }
            }
        }
        return count;
    }

    private static int findFirstIndex(String formula, int startIndex, int endIndex, String... chars) {
        String substring = formula.substring(startIndex, endIndex);

        List<Integer> integers = new ArrayList<>();

        for (String s : chars) {
            int index = substring.indexOf(s);
            if (index >= 0) {
                integers.add(index);
            }
        }

        if (integers.isEmpty()) {
            return -1;
        }
        return integers.stream().min((i1, i2) -> i1 - i2).get() + startIndex;
    }

    private static int findLastIndex(String formula, int startIndex, int endIndex, String... chars) {
        String substring = formula.substring(startIndex, endIndex);

        List<Integer> integers = new ArrayList<>();

        for (String s : chars) {
            int index = substring.lastIndexOf(s);
            if (index >= 0) {
                integers.add(index);
            }
        }

        if (integers.isEmpty()) {
            return -1;
        }
        return integers.stream().max((i1, i2) -> i1 - i2).get() + startIndex;
    }

    /**
     * 找到左括号对应的右括号位置
     * @param formula 表达式
     * @param leftIndex 左括号索引位置
     * @return 右括号位置
     */
    private static int getMirrorIndex(String formula, int leftIndex) {
        int num = 1;
        int leftStart = leftIndex + 1;

        while (leftStart > 0) {
            int nextIndex = formula.indexOf("(", leftStart);
            if (nextIndex < 0) {
                break;
            }
            String s = formula.substring(leftStart, nextIndex);
            if (s.contains(")")) {
                break;
            }
            num++;
            leftStart = nextIndex + 1;
        }

        int rIndex = -1;
        for (int i = 0; i < num; i++) {
            int ri = formula.indexOf(")", rIndex + 1);
            if (ri < 0) {
                break;
            }
            rIndex = ri;
        }
        return rIndex;
    }

    /**
     * 公式定义
     */
    @Data
    static class Formula {

        private static final ThreadLocal<Integer> number = ThreadLocal.withInitial(() -> 0);

        private static String varRegex = "^\\$\\{\\S+\\}$";

        private static String symbolRegex = "[\\+\\-\\*/]";

        private static String numberRegex = "^[\\+\\-]*\\d+\\.*\\d*$";

        private static String varChar = "[\\$\\{\\}]";

        /**
         * 该表达式结果的变量名
         */
        private String variable;

        /**
         * 表达式
         */
        private String formulaStr;

        public Formula(String formulaStr) {
            this.variable = "var" + number.get();
            this.formulaStr = formulaStr;
            // 变量每次加1
            number.set(number.get() + 1);
        }

        public String getVariableStr() {
            return "${" + this.variable + "}";
        }

        public Number calculate(Map<String, Number> varMap) {
            String[] fs = formulaStr.split(symbolRegex);
            if (fs.length == 1) {
                String var = fs[0];
                return getVarValue(var, varMap);
            }

            if (fs.length != 2) {
                throw new IllegalArgumentException("表达式错误: " + formulaStr + ",+ - * / 符号两侧必须是变量或者常量");
            }

            String var1 = fs[0];
            String var2 = fs[1];
            // 运算符号
            String symbol = formulaStr.charAt(var1.length()) + "";

            BigDecimal b1 = getVarValue(var1, varMap);
            BigDecimal b2 = getVarValue(var2, varMap);

            BigDecimal res;
            if ("+".equals(symbol)) {
                res = b1.add(b2);
            } else if ("-".equals(symbol)) {
                res = b1.subtract(b2);
            } else if ("*".equals(symbol)) {
                res = b1.multiply(b2);
            } else if ("/".equals(symbol)) {
                if (b2.compareTo(new BigDecimal("0")) == 0) {
                    throw new IllegalArgumentException("被除数 ${" + var2 + "}: 不能为0");
                }
                res = b1.divide(b2, 8, BigDecimal.ROUND_HALF_DOWN);
            } else {
                throw new IllegalArgumentException("运算符号:" + symbol + " 错误,支持: + - * /");
            }

            return res;
        }

        private BigDecimal getVarValue(String var1, Map<String, Number> varMap) {
            Number n1;
            if (var1.matches(varRegex)) {
                var1 = var1.replaceAll(varChar, "");
                n1 = varMap.get(var1);
                if (n1 == null) {
                    throw new IllegalArgumentException("找不到变量:" + var1 + " 的值,请检查");
                }
            } else if (var1.matches(numberRegex)) {
                n1 = Double.valueOf(var1);
            } else {
                throw new IllegalArgumentException("表达式:" + var1 + ",不符合 变量或者常量格式,如:${a}、2、2.3、-5");
            }
            return BigDecimal.valueOf(n1.doubleValue());
        }

        public static void main(String[] args) {
            Formula f = new Formula("${a}*2.3");
            Map<String, Number> numberMap = new HashMap<>();
            numberMap.put("a", 1.1);
            numberMap.put("v", 2);

            Number calculate = f.calculate(numberMap);
            System.out.println(calculate);
        }

        @Override
        public String toString() {
            return variable + " = " + formulaStr;
        }
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:06:43  更:2021-08-09 10:07:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 19:38:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码