02 计算字符串表达式 [类似于js eval函数]

原创 2015年11月21日 15:43:08

前言

这个 也是之前的时候在贴吧里看到的一个小题目, 无聊的时候引起了兴趣, 于是 当时就做了做, 但是 当时思考的时候, 设计的太细节化了[对于()之间的表达式, 现在 我绝对是递归计算, 而并非将其提取出来单独计算], 所以 导致存在很多错误, 但是 后来再来看这个的时候, 发现 居然有四百多行, 顿时就没了改正的心情了
原帖 : http://tieba.baidu.com/p/3787837745

/**
 * file name : Test03EvalLike.java
 * created at : 8:53:59 AM May 14, 2015
 * created by 970655147
 */

最近 闲来无事看看以前的代码的时候, 突然想起了, 就将这个问题重新思考了一下

思路如下

对于 一个字符串的表达式, 如果需要计算他的话, 首先需要解析其中的操作符, 以及操作数
这里写图片描述

1 首先 需要解析各个符号
对于 “+-*/%” 获取该符号 以及位置
对于”()”递归eval”(“, “)”之间的表达式, 存储起来

2 业务处理
2.1) 初始值的处理
如果lastOptNow为null
    判断第一个符号是否是”(“, 如果是则获取第一个()之间的结果, 作为初始值[入口 : 1. eval, 2. 下一个符号为”(” 并且下下一个符号的优先级比当前运算符的优先级高]
        否则 解析第一个符号左边的数据作为操作数[入口 : 1. eval]
    否则 如果给定了lastOptNow 解析lastOptNow到第一个符号之间的数据作为初始值 [入口 : 1. 下一个操作符的优先级比当前操作符优先级高, 并且下一个操作符不为”(” ]

2.2) 计算表达式[计算最后一个操作符之前的表达式]
获取下一个操作符, 以及下下一个操作符
如果下一个操作符的优先级比下下一个操作符的优先级高
    如果下一个操作符为”(“,
        如果当前”(“的对应的下一个”)”之后的下一个操作符的优先级比当前操作符的优先级高 计算当前结果和剩下的其他表达式的结果, 返回结果
            否则 计算当前结果 和()之间的计算结果进行计算, 进入下一个循环
        否则 计算当前结果和剩下的其他表达式的结果, 返回结果
    否则 更新当前结果为当前结果和下一个操作数之间的计算结果

2.3) 如果剩余的操作符列表大于1个操作符 计算最后一个操作符

2.4) 返回结果

参考代码

/**
 * file name : Test07Eval.java
 * created at : 5:12:16 PM Nov 20, 2015
 * created by 970655147
 */

package com.hx.test08;

public class Test07EvalLike {

    // main
    public static void main(String []args) {

        // 1. 简单的测试
//      String exp = "2 * ( 3 + 4 )";
//      String exp = "2 * ( 3 + 4 ) + (3 * 5)";
//      String exp = "3 * 2 * ( 3 + 4 )";
//      String exp = "3 + 3 * 2 * ( 3 + 4 ) + 2";
        String exp = "3 + 3 * 2 * ( 3 + (3 * 6) + 4 ) + 2";
//      String exp = "3 * 80 + 81 * 93";
//      String exp = "3 + 3 * 2 * ( 3 + 4 )";
//      String exp = "(3 * 2) * ( 3 + 4 )";
        // ...
//      String exp = "91 * 32 - 8 - 2";

        int val = eval(exp);

        System.out.println(val);


        // 2. 多次随机测试
//      randomTest();

    }

    // 各个符号的常量, 匹配的符号映射, 各个符号的优先级
    private final static char ADD = '+';
    private final static char SUB = '-';
    private final static char MUL = '*';
    private final static char DIV = '/';
    private final static char MOD = '%';
    private final static char LEFT_BRACKET = '(';
    private final static char RIGHT_BRACKET = ')';
    private final static Map<Character, Character> matched = new HashMap<>();
    private static Map<Character, Integer> priority = new HashMap<>();

    // 初始化
    static {
        matched.put(LEFT_BRACKET, RIGHT_BRACKET);

        priority.put(ADD, 0);
        priority.put(SUB, 0);
        priority.put(MUL, 1);
        priority.put(DIV, 1);
        priority.put(MOD, 1);
        priority.put(LEFT_BRACKET, 2);
        priority.put(RIGHT_BRACKET, 2);
    }

    // 先统计出exp中各个符号的位置  在用eval0真正计算
    // 首先记录所有的符号的位置, 添加到一个符号列表中[如果 有子计算过程, 递归计算]
    // 然后  计算给定的表达式
    public static int eval(String exp) {
        // Operator, Operation
        List<Integer> resInBrackets = new LinkedList<>();
        List<Operator> optr = new LinkedList<Operator>();
        for(int i=0; i<exp.length(); i++) {
            char c = exp.charAt(i);
            switch (c ) {
                case ADD :
                case SUB :
                case MUL :
                case DIV :
                case MOD :
                    optr.add(new Operator(c, i));
                    break;
                case LEFT_BRACKET :
                    int nextI = getNextMatched(exp, i+1, LEFT_BRACKET);
                    resInBrackets.add(eval(exp.substring(i+1, nextI)) );
                    optr.add(new Operator(LEFT_BRACKET, i) );
                    optr.add(new Operator(RIGHT_BRACKET, nextI) );
                    i = nextI;
                default :
                    break;
            }
        }

//      while(optr.size() > 0) {
//          System.out.println(optr.pop());
//      }

        int val = eval0(exp, optr, resInBrackets, null);


        return val;
    }

    // 计算给定的表达式  从首 -> 尾
    // 获取第一个操作数作为默认的返回值
        // 然后 获取第一个操作符, 以及下一个操作符
        // 如果下一个操作符的优先级大于当前操作符, 递归计算(当前结果, 第一个操作符之后的部分) [这里可以为本方法添加两个参数, 将带return的递归构造成尾递归, 不过这里  就不做了]
            // 否则  计算第一个操作符旁边的两个操作数, 循环, 直到操作符列表中只剩下一个操作符
    // 计算最后一个操作符  二边的操作数
    private static int eval0(String exp, List<Operator> optr, List<Integer> resInBrackets, Operator lastOptNow) {
        int res = 0;
        if(lastOptNow == null) {
            Operator firstOpt = optr.get(0);
            if(firstOpt.oper == LEFT_BRACKET) {
                optr.remove(0);
                optr.remove(0);
                res = resInBrackets.remove(0);
            } else {
                res = parseFirstInt(exp, firstOpt );
            }
        } else {
            res = parseInt(exp, lastOptNow, optr.get(0) );
        }

        while(optr.size() > 1) {
            Operator optNow = optr.get(0);
            Operator optNext = optr.get(1);
            optr.remove(0);
            if(priority.get(optNext.oper) > priority.get(optNow.oper) ) {
                if(optNext.oper == LEFT_BRACKET) {
                    Operator optNNext = null;
                    if(optr.size() > 2) {
                        optNNext = optr.get(2);
                    }
                    if((optNNext != null) && (priority.get(optNNext.oper) > priority.get(optNow.oper)) ) {
                        return calc(res, optNow, eval0(exp, optr, resInBrackets, null) );
                    } else {
                        optr.remove(0);
                        optr.remove(0);
                        res = calc(res, optNow, resInBrackets.remove(0) );
                    }
                } else {
                    return calc(res, optNow, eval0(exp, optr, resInBrackets, optNow) );
                }
            } else {
                res = calc(res, optNow, parseInt(exp, optNow, optNext) );
            }
        }

        if(optr.size() > 0) {
            res = calc(res, optr.get(0), parseLastInt(exp, optr.get(0)) );
        }

        return res;
    }

    // 计算val01, val02  的opt操作  返回结果
    private static int calc(int val01, Operator opt, int val02) {   
        int val = 0;
        switch(opt.oper) {
            case ADD :
                val = val01 + val02;
                break;
            case SUB :
                val = val01 - val02;
                break;
            case MUL :
                val = val01 * val02;
                break;
            case DIV :
                val = val01 / val02;
                break;
            case MOD :
                val = val01 % val02;
                break;
        }

        return val;
    }

    // 解析当前云算符 和下一个云算法之间的的操作数
    private static int parseInt(String exp, Operator optNow, Operator optNext) {
        String intStr = null;
        try {
            intStr = exp.substring(optNow.index+1, optNext.index).trim();
            return Integer.parseInt(intStr);
        } catch (Exception e) {
            Log.err("error while parse first operand[idx : " + optNow.index + "][' " + intStr + " '] !");
            throw e;
        }
    }
    private static int parseLastInt(String exp, Operator optNow) {
        try {
            return Integer.parseInt(exp.substring(optNow.index+1).trim() );
        } catch(Exception e) {
            Log.err("error while parse last operand[idx : " + optNow.index + "] !");
            throw e;
        }
    }
    private static int parseFirstInt(String exp, Operator optNow) {
        try {
            return Integer.parseInt(exp.substring(0, optNow.index).trim() );
        } catch(Exception e) {
            Log.err("error while parse first operand[idx : " + optNow.index + "] !");
            throw e;
        }
    }

    // 获取当前位置的符号匹配的下一个符号
    private static int getNextMatched(String exp, int idx, char left) {
        Deque<Character> stack = new LinkedList<>();
        stack.push(left);
        for(int i=idx; i<exp.length(); i++) {
            char ch = exp.charAt(i);
            if(matched.containsKey(ch) ) {
                stack.push(ch);
            }
            if(ch == matched.get(stack.peek()) ) {
                stack.pop();
                if(stack.size() == 0) {
                    idx = i;
                    break ;
                }
            }
        }

        return idx;
    }

    // --------------- bean --------------------

    // 封装每一个操作符   以及其索引
    static class Operator {
        // 操作符, 索引
        char oper;
        int index;

        // 初始化
        public Operator() {

        }
        public Operator(char oper, int index) {
            this.oper = oper;
            this.index = index;
        }

        // for debug ..
        public String toString() {
            return oper + " -> " + index + "; ";
        }
    }

}

效果截图
这里写图片描述

这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

字符串表达式的计算

由于最近要笔试,所以试着做了些笔试题,把自己做的分享下。题目:有个字符串:“123*67+789-43/12*563+234*678-234/67+132-1+490*12/33”,请编写代码将字符串...

javascript:逆波兰式表示法计算表达式结果

博客园 2016-11-28 21:13 逆波兰式表示法,是由栈做基础的表达式,举个例子: 5 1 2 + 4 * + 3 - 等价于 5 + ((1 + 2) * 4) - 3 原理:...

js eval替代方法学习笔记

写前端的小伙伴们都知道,js种eval()方法是用来把一段字符串转换成js代码并执行。但是eval的执行效率非常差,并且容易造成作用于混乱。所以非常不提倡使用eval,甚至要避免使用。 但是在某些情况...
  • guo8ke
  • guo8ke
  • 2017-04-28 10:07
  • 1397

c#中实现类似js的Eval|.NET中执行Javascript(表达式是字符串的计算)

我们一般只知道javascript中有个eval方法,其实在c# 中也有这样一个方法,下面我们讲解c#中Eval的使用方法。   第一步在你的项目中添加Microsoft.Vsa和Microsoft....

java字符串表达式计算

java基于栈的方式计算字符串表达式

输入一个字符串表达式,输出计算结果(队列、栈的应用)

#include #include #include #include using namespace std; string input; queue q; stack ope...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)