缘由
为了解决编程之美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个人最好。我觉得我没事也可以动动这方面的脑子,看看有什么好的点子。