一、程序要求
解析一般数学算式,实现简单的带括号的加减乘除运算。
二、基本思路
上一篇我以正常思考计算式子的角度考虑,介绍了直接递归遍历字符串解析数学四则运算式子,思路很好理解,但程序写出来较为难读。这里再用另一种方法,采用java的ArrayList集合(数组)来实现目的。
这次我从易到难考虑:
- 1)、单个数:如3,运算结果即这个数
- 2)、加入加减后运算:如1+6-4,从左往右依次加减,式子将变为1)
- 3)、加入乘除后运算:如1+2*3-4,从左往右依次先做乘除,式子将会变为2)
- 4)、加入括号后运算:如1*(1+2*3-4)+5,单独拿出括号中的内容,即3),将计算结果替换括号1*3+5,又变成3)的情况
因此,对于一个数字式子,我们应该考虑化繁为简、由难变易。往往加入括号后,让我们解析起来增加了困难,所以,解析无括号的四则运算是我们应该考虑的第一步。
对于一个无括号的数字式子:-1+2*-3/3+-2 :
- 1、把数字和运算符分离,分别放入两个数组:
- 2、遍历符号数组,先从左向右找乘除,如上b1为第一个乘除号,则删除b1、a1、a2,将a1 b1 a2的运算结果插入到a1;以此类推至无乘除号:
- 3、同2,从左往右依次计算加减至无加减:
- 4、此时a0即为运算结果
对于一个有括号的式子,我们只有按无括号的计算方法计算出括号中的值,从里到外将括号替换为计算结果即可,再进一步化简为整个的无括号的式子!
三、代码
环境:
- Eclipse Java EE IDE(Version: Oxygen.1a Release (4.7.1a))
- jdk1.8.0_131
先写一个最基本的两位数四则运算方法,比较简单,没有写注释:
public static double doubleCal(double a1, double a2, char operator) throws Exception {
switch (operator) {
case '+':
return a1 + a2;
case '-':
return a1 - a2;
case '*':
return a1 * a2;
case '/':
return a1 / a2;
default:
break;
}
throw new Exception("illegal operator!");
}
计算无括号的四则运算:
private static String noBrackets(String expr) throws Exception {
//这个正则是匹配是不是符合无括号表达式的规则,即 数字+运算符+数字+运算符....
if (!expr.matches("-?\\d+(\\.\\d+)?([+\\-*/]-?\\d+(\\.\\d+)?)*")) {
throw new Exception("Wrong format!");
}
System.out.println("计算:"+expr);
/* 盛放运算数 */
List<Double> number = new ArrayList<Double>();
/* 盛放运算符 */
List<Character> operator = new ArrayList<Character>();
/* 将expr打散分散到运算数和运算符数组 */
//这个正则为匹配表达式中的数字
Pattern p = Pattern.compile("(?<!\\d)-?\\d+(\\.\\d+)?");
Matcher m = p.matcher(expr);
while (m.find()) {
number.add(Double.valueOf(m.group()));//将该数字放进数字的数组
if (m.end() < expr.length()) {
operator.add(expr.charAt(m.end()));//与该数字相邻的运算符放入运算符的数组
}
}
System.out.println("计算数数组:" + number);
System.out.println("计算符数组:" + operator);
/*
* 先算乘除,再算加减 这里利用了逻辑或||的运算法则,正好对应计算式子的乘除加减的顺序
*/
int i = -;
while (- != (i = operator.indexOf('*')) || - != (i = operator.indexOf('/'))
|| - != (i = operator.indexOf('+')) || - != (i = operator.indexOf('-'))) {
char c = operator.get(i);
operator.remove(i);
double a1 = number.remove(i);
double a2 = number.remove(i);
number.add(i, doubleCal(a1, a2, c));
}
String res = number.get()+"";
System.out.println("此步结果:"+res);
return res;
}
计算带括号的四则运算:
public static String getResult(String expr) throws Exception {
System.out.println("开始计算:"+expr);
/*表示最后一个左括号*/
int lIndex = -;
/*与该左括号对应的右括号*/
int rIndex = -;
/*去括号*/
while(- != (lIndex = expr.lastIndexOf("("))) {
rIndex = expr.indexOf(")",lIndex);
expr = expr.substring(,lIndex)+noBrackets(expr.substring(lIndex+, rIndex))+expr.substring(rIndex+);
}
/*计算结果*/
return noBrackets(expr);
}
测试方法:
public static void main(String[] args) throws Exception {
String str = "-3.5*(4.5-(4+(-1-1/2)))";
System.out.println(getResult(str));
}
四、运行结果
五、分析
这种方法整体思路和第一种差不多,理解算法后程序更易于读懂,但对数组需要多次遍历和移位,效率上仍不是很高。为了进一步优化,换一种数据结构“栈”来存取数据可以达到更好的执行效果。