java代码:计算以 字符串 形式存在的算术表达式

缘由

为了解决编程之美24点游戏(1.16)引申出来的问题。
直观一点来看:
有这样一个表达式:
  • String expr = "(2+1)*2"; 
如何计算其值?

解决方案

核心是用别的东西来代表运算符:+ - * / ( ),然后我们再模拟进行人在计算中的过程。就拿上面的(2+1)*2来说,我们首先用将里面的运算符和括号转换一下,转换成一个标示,必须转换的原因是这样我们才能和操作符一起存储在一个double类型的动态数组里面,这样我们才能逐一解析这个动态数组,如下所示:

static double ADD =  Math.PI;      //加  
static double SUB =  Math.PI + 1;  //减  
static double MUL =  Math.PI + 2;  //乘  
static double DIV =  Math.PI + 3;  //除  
static double LBR =  Math.PI + 4;  //左括号  
static double RBR =  Math.PI + 5;  //右括号

转化后的形式即为:LBR 2 ADD 1 RBR MUL 2 ,是用一个动态数组ArrayList来保持这一系列的数。
然后我们再逐一解析这个算术式,比如先计算括号内的,那么我们可以定位LBR和RBR,再将里面的2 ADD 1给计算出来,用结果替换整个LBR 2 ADD 1 RBR。然后接着计算,注意没有括号之后,就是先算乘法。大概流程如上所述,下面是代码部分。

代码

代码是从别的博客上找的,不是我自己写的,但是我找不到原博客地址了,对不起原作者了。
import java.util.*;  


public class String2expr
{     
	static double ADD =  Math.PI;      //加  
	static double SUB =  Math.PI + 1;  //减  
	static double MUL =  Math.PI + 2;  //乘  
	static double DIV =  Math.PI + 3;  //除  
	static double LBR =  Math.PI + 4;  //左括号  
	static double RBR =  Math.PI + 5;  //右括号  
    //按表达式中出现的次序存储提取出的操作数和操作符  
    static ArrayList<Double> al = new ArrayList<Double>();  
  
    public static void main(String[] args)   
    {  
        //要计算的字符串表达式  
        String expr = "(2+1)*2";  
        int rslt = (int)evalbr(expr);  
        System.out.println(rslt);  
    }  
  
    //计算含括号的表达式的值  
    public static double evalbr(String expr)  
    {  
        //解析这个expr表达式,结果存入arraylist  
        parse(expr);  
  
        //显示解析后的arraylist中的表达式  
        //display();  
          
        //左右括号位置  
        int lbr_pos = 0;  
        int rbr_pos = 0;  
          
        //临时存放括号对内表达式计算结果  
        double rslt = 0;  
  
        //如果表达式中还有括号的话,就从右向左继续计算各个括号对里表达式的值,并替换化简  
        while(al.contains(LBR))  
        {  
            //最右边的左括号位置  
            lbr_pos = al.lastIndexOf(LBR);  
            //其对应的右括号  
            rbr_pos = lbr_pos + 1;  
            while(al.get(rbr_pos) != RBR)   rbr_pos++;  
            //计算该括号对内表达式的值, 并替换化简  
            eval(lbr_pos + 1, rbr_pos - 1);  
            //删除括号对  
            al.remove(lbr_pos + 2);  
            al.remove(lbr_pos);  
            //display();  
        }  
  
        //计算所有括号化简掉后的表达式的值  
        eval(0, al.size() - 1);  
  
        //化简到最后剩下的唯一一个操作数就是答案  
        return  al.get(0);  
    }  
  
    //计算不含括号的表达式的值, 该表达式表示为al中从start到end的一段元素  
    public static void eval(int start, int end)  
    {  
        //元素在当前化简表达式中的位置  
        int i = start;  
        //化简的停止位置  
        int stop = end;  
  
        //从左向右计算乘除法,每消去一个操作符则更新al  
        while(i <= stop)  
        {  
            double element = al.get(i);  
            //临时存放结果  
            double rslt = 0;  
              
            //如果是乘除操作符,则计算该符结果并更新表达式为化简后的  
            if(element == MUL)      
            {  
                rslt = al.get(i - 1) * al.get(i + 1);  
                //删除操作符和右操作数,左操作数用本步计算结果代替  
                al.remove(i + 1);  
                al.remove(i);  
                al.set(i - 1, rslt);  
                stop = stop - 2;  
                //display();  
            }  
            else if(element == DIV)      
            {  
                rslt = al.get(i - 1) / al.get(i + 1);  
                al.remove(i + 1);  
                al.remove(i);  
                al.set(i - 1, rslt);  
                stop = stop - 2;  
                //display();  
            }  
            else  
            {  
                i++;  
            }  
        }  
  
        i = start;  
        //从左向右计算加减法,每消去一个操作符则更新al  
        while(i <= stop)  
        {  
            double element = al.get(i);  
            //临时存放结果  
            double rslt = 0;  
              
            //如果是乘除操作符,则计算该符结果并更新表达式为化简后的  
            if(element == ADD)      
            {   rslt = al.get(i - 1) + al.get(i + 1);  
                al.remove(i + 1);  
                al.remove(i);  
                al.set(i - 1, rslt);  
                stop = stop - 2;  
                //display();  
            }  
            else if(element == SUB)      
            {  
                rslt = al.get(i - 1) - al.get(i + 1);  
                al.remove(i + 1);  
                al.remove(i);  
                al.set(i - 1, rslt);  
                stop = stop - 2;  
                //display();  
            }  
            else  
            {  
                i++;  
            }  
        }  
    }  
  
    //提取字符串中的操作符和操作数并存储到al中  
    public static void parse(String expr)  
    {  
        //去掉所有空格  
        String str = expr.replace(" ", "");  
  
        //标记单目操作符负号位置  
        int minus_pos = 0;  
  
        //将表达式中的所有单目运算符负号转化成双目运算符减号,补充上左操作数0  
        while( (minus_pos = str.indexOf("(-")) !=  - 1)  
        {  
            str = str.substring(0, minus_pos + 1) + "0" + str.substring(minus_pos + 1);  
        }  
  
        //一个操作数、操作符的开始位置  
        int pos_start = 0;    
        //一个操作数、操作符的结束位置  
        int pos_end = 0;      
        //将字符串转换为字符数组  
        char[] arr = str.toCharArray();  
  
        //逐个提取表达式中的操作数和操作符  
        while(pos_start != arr.length )  
        {  
            pos_end = pos_start;  
            while( Character.isDigit(arr[pos_start]) == Character.isDigit(arr[pos_end]) || arr[pos_end] == '.')  
            {  
                pos_end++;  
                if(pos_end == arr.length)  
                {  
                    break;  
                }  
            }  
              
            //提取到的操作符或操作数字符串  
            String tmp = new String(arr, pos_start, pos_end - pos_start);  
              
            if(Character.isDigit(arr[pos_start]))  
            {  
                //将提取出的操作数放入arraylist  
                al.add(Double.parseDouble(tmp));  
            }  
            //将提取出的操作符放入arraylist  
            else  
            {  
                char[] op = tmp.toCharArray();  
                for(char c : op)  
                {  
                    if(c == '+')  
                        al.add(ADD);  
                    else if(c == '-')  
                        al.add(SUB);  
                    else if(c == '*')  
                        al.add(MUL);  
                    else if(c == '/')  
                        al.add(DIV);  
                    else if(c == '(')  
                        al.add(LBR);  
                    else if(c == ')')  
                        al.add(RBR);  
                    else  
                        throw new RuntimeException("Operator not allowd : " + tmp);  
                }  
            }  
            pos_start = pos_end;  
        }  
    }  
  
    //显示化简到当前一步的表达式  
    public static void display()  
    {  
        //输出提取到的所有元素‘  
        for(int i = 0; i < al.size(); i++)  
        {  
            if(al.get(i) == ADD)  
                System.out.print("+");  
            else if(al.get(i) == SUB)  
                System.out.print("-");  
            else if(al.get(i) == MUL)  
                System.out.print("*");  
            else if(al.get(i) == DIV)  
                System.out.print("/");  
            else if(al.get(i) == LBR)  
                System.out.print("(");  
            else if(al.get(i) == RBR)  
                System.out.print(")");  
            else  
                System.out.print(al.get(i));  
        }  
            System.out.println("\n");  
    }  
}  

杂记

昨天突然觉得还是原来一天一篇博客来的效果好。虽然压力觉得比较大些。但是最近又比较忙,因为忙着期末考试,忙着找实习,忙着改简历,忙着准备六级。所以恐怕这个时候,再一天一博客就很费劲了。从心里来讲,真心喜欢编码,感觉给我一个需求文档让我慢慢编,很享受的感觉。所以还是很喜欢在公司里面干活的。
又由于看了《黑客与画家》,对lisp这个语言产生了极大的好奇,所以恐怕不学就忍不住了。看了看《黑客与画家》的作者的经历,所以觉得自己还是随心而行算了,不要太追求一时的结果,做自己有兴趣的事最好。里面除了引起了我对lisp这个语言的好奇以外,最大的感触就是创业的问题。好像在it行业,创业不是那么需要人哈,里面提到的从技术角度来讲3个人最好。我觉得我没事也可以动动这方面的脑子,看看有什么好的点子。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值