可以进行加减乘除,指数运算,幂运算,阶乘运算,三角函数运算,对数函数运算。
由于前面几种比较好处理,所以在这里我主要阐述对于三角函数和对数函数的运算,可以采用递归的思想。
( 为了方便说明,本文直接使用一个例子来说明,下标都从0开始计数 )
首先,有个一个原始表达式,如 sin(1+2)*ln(2*3)+5。这个表达式很难用一次性全部计算出来,但可以把不含有函数的中缀表达式( 如sin(1+2)里面的1+2 ,sin(1+2)这样的表达式本文将此称为子表达式)转换为后缀表达式,然后再运算。
在这里我们定义四个栈,这四个栈分别是 运算表达式的表达式栈,子表达式的在其父表达式中的两个位置栈(对于sin(1+2),在其父表达式的第0个和第7个,两个栈分别存了0和7),子表达式的所代表的函数栈(对于sin(1+2),栈中存储的就是"sin")
Stack<String> functionStack = new Stack<>();//存表达式
Stack<Integer> leftStack = new Stack<>();//存子表达式左边
Stack<Integer> rightStack = new Stack<>();//存子表达式右边
Stack<String> numStack = new Stack<>();//存子表达式的函数
String currentStr;//当前表达式,所有操作都是基于当前表达式
然后现在就有个问题,首先怎么定位子表达式,本文采用的是依此遍历,从左到右依次扫描字符串,如果遇到sin,cos,tan,ln这四种字符串就返回其在父字符串的位置,分别push进leftStack和rightStack中,再把父表达式push进functionStack中,再把子表达式的函数push进numStack中。并把当前字符串设置为不含有函数的中缀表达式( 如sin(1+2)里面的1+2,算出1+2后,把sin(3)替换父字符串(0.141*ln(2*3)+5),其他都是照葫芦画瓢了。
functionStack.push(currentStr);
int begin = functionBorder(currentStr);
int end = functionBorder(currentStr, begin);
leftStack.push(begin);
rightStack.push(end);
numStack.push(numOfFunction(currentStr));
currentStr = currentStr.substring(begin + numStack.peek().length() + 1, end);//得到这个函数里的字符串
因此,我们可以把sin(1+2)和ln(2*3)算出来,这样这个表达式就很好计算了,那就是0.141*1.098+5,这个式子就很简单了,这就是个标准只有加减乘除的表达式了。
还有人可能说,如果是函数嵌套(如:sin(cos(x)) )的话怎么算呢?这就是递归的魅力了,这就和递归树的后序遍历一样了。
public static String calculate(String currentStr) {
Stack<String> functionStack = new Stack<>();//存表达式
Stack<Integer> leftStack = new Stack<>();//存子表达式左边
Stack<Integer> rightStack = new Stack<>();//存子表达式右边
Stack<String> numStack = new Stack<>();//存子表达式的函数
while (!isNumber(currentStr) || !functionStack.isEmpty()) {
if (containsSpecialStr(currentStr)) {//存在特殊函数
functionStack.push(currentStr);
int begin = functionBorder(currentStr);
int end = functionBorder(currentStr, begin);
leftStack.push(begin);
rightStack.push(end);
numStack.push(numOfFunction(currentStr));
currentStr = currentStr.substring(begin + numStack.peek().length() + 1, end);//得到这个函数里的字符串
//functionStack.push(currentStr);
} else {//不存在特殊函数
double calculate = Double.parseDouble(calculateWithoutStr(currentStr));//这个函数就是计算简单的中缀表达式的
String suan = "";
if (!numStack.isEmpty())//没有特殊函数的情况下
suan = numStack.pop();
switch (suan) {
case "cos":
calculate = Math.cos(calculate);
break;
case "sin":
calculate = Math.sin(calculate);
break;
case "tan":
calculate = Math.tan(calculate);
break;
case "ln":
calculate = Math.log(calculate);
break;
}
if (!functionStack.isEmpty()) {
currentStr = new StringBuilder(functionStack.pop()).replace(leftStack.pop(),
rightStack.pop() + 1, String.valueOf(calculate)).toString();
} else
currentStr = String.valueOf(calculate);
}
}
return currentStr;
}
//正则表达式 确认表达式是否为浮点数
public static boolean isNumber(String str) {
String reg = "^[-+]?[0-9]+(.[0-9]+)?$";
return str.matches(reg);
}
private static boolean containsSpecialStr(String str) {
return str.contains("sin") || str.contains("cos") || str.contains("ln") || str.contains("tan");
}
private static int functionBorder(String expression) {
char[] chars = expression.toCharArray();
for (int j = 0; j < chars.length; j++) {//+ - * / ^ ! ( )
if (!(isDigit(chars[j]) || chars[j] == '+' || chars[j] == '-' || chars[j] == '*' || chars[j] == '/' ||
chars[j] == '!' || chars[j] == '^' || chars[j] == '(' || chars[j] == ')' || chars[j] == '.')) {
return j;//如果是字母就返回,得到起始字符
}
}
return -1;//表示无字母
}
private static String numOfFunction(String expression) {
char[] chars = expression.toCharArray();
for (char aChar : chars) {
if (aChar == 'c')//如果是字母就返回,得到起始字符
return "cos";
else if (aChar == 's')
return "sin";
else if (aChar == 't')
return "tan";
else if (aChar == 'l')
return "ln";
}
return "";//表示无字母
}
private static int functionBorder(String expression, int begin) {//从0开始
int leftParenthesis = 1;
char current;
int end = begin;
boolean flag = true;
while (leftParenthesis != 0 && end + 1 != expression.length()) {
end = end + 1;
current = expression.charAt(end);
if (current == '(') {
leftParenthesis++;
if (flag) {
leftParenthesis--;
flag = false;
}
} else if (current == ')')
leftParenthesis--;
}
if (leftParenthesis != 0)
throw new RuntimeException("表达式出错,括号少了");
return end;
}
这个函数里除了计算简单中缀表达式的函数,其他都给了
double calculate = Double.parseDouble(calculateWithoutStr(currentStr));
就只有calculateWithoutStr()这个函数没给,这网上随随便便都找得到。