(演算法)java完成解析數學算式(計算器)一 —— 遞迴、正則直接遍歷字串解析
阿新 • • 發佈:2019-02-04
一、程式要求
解析一般數學算式,實現簡單的帶括號的加減乘除運算。
二、基本思路
先從我們人的角度,考慮平時在計算一個式子的思路,任意假設一個的數學表示式-3.5*(4.5-(4+(-1-1/2)))
- 1、計算最裡面的括號(最後一個左括號)裡的表示式(-1-1/2)
- 2、計算除法1/2,將括號內容變為(-1-0.5)
- 3、計算減法,得出括號結果-1.5
- 4、用該結果將括號替換,表示式變為-3.5*(4.5-(4±1.5))
- 5、重複1-4步,去掉剩下的兩個括號,將表示式變為-3.5*2
- 6、算一步乘法,得-7,除了首位正負號外,不含其它符號,即運算結束,-7為運算結果
簡單總結,即**先去括號至無括號,再去乘除至無乘除,最後去加減至無加減
三、程式碼
環境:
- 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!"); }
解析運算表示式的方法:
public static String getResult(String str) throws NumberFormatException, Exception { //處理一下計算過程中出現的--情況,首位--直接去掉,中間--變為+ str = str.startsWith("--") ? str.substring(2) : str; str = str.replaceAll("--", "+"); str = str.replaceAll("\\+-", "-"); System.out.println("新表示式:"+str); if (str.matches("-{0,1}[0-9]+([.][0-9]+){0,1}"))//不存在運算子了,即遞迴結束,這裡的正則為匹配所有的正負整數及小數 return str; /*表示每次遞迴計算完一步後的表示式*/ String newExpr = null; // 第一步:去括號至無括號 if (str.contains("(")) { /*最後一個左括號的索引值*/ int lIndex = str.lastIndexOf("("); /*該左括號對應的右括號的索引*/ int rIndex = str.indexOf(")", lIndex); /*括號中的字表達式*/ String subExpr = str.substring(lIndex + 1, rIndex); System.out.println("準備括號:("+subExpr+")"); newExpr = str.substring(0, lIndex) + getResult(subExpr) //呼叫本身,計算括號中表達式結果 + str.substring(rIndex + 1); return getResult(newExpr); } // 第二步:去乘除至無乘除 if (str.contains("*") || str.contains("/")) { /*該正則表示匹配一個乘除運算,如1.2*3 1.2/3 1.2*-2 等*/ Pattern p = Pattern.compile("[0-9]+([.][0-9]+){0,1}[*/]-{0,1}[0-9]+([.][0-9]+){0,1}"); Matcher m = p.matcher(str); if (m.find()) { /*第一個乘除表示式*/ String temp = m.group(); System.out.println("計算乘除:"+temp); String[] a = temp.split("[*/]"); newExpr = str.substring(0, m.start()) + doubleCal(Double.valueOf(a[0]), Double.valueOf(a[1]), temp.charAt(a[0].length())) + str.substring(m.end()); } return getResult(newExpr); } // 第三步:去加減至無加減 if (str.contains("+") || str.contains("-")) { /*該正則表示匹配一個乘除運算,如1.2+3 1.2-3 1.2--2 1.2+-2等*/ Pattern p = Pattern.compile("-{0,1}[0-9]+([.][0-9]+){0,1}[+-][0-9]+([.][0-9]+){0,1}"); Matcher m = p.matcher(str); if (m.find()) { /*第一個加減表示式*/ String temp = m.group(); System.out.println("計算加減:"+temp); String[] a = temp.split("\\b[+-]", 2); newExpr = str.substring(0, m.start()) + doubleCal(Double.valueOf(a[0]), Double.valueOf(a[1]), temp.charAt(a[0].length())) + str.substring(m.end()); } return getResult(newExpr); } throw new Exception("Calculation error"); }
主方法:
public static void main(String[] args) throws Exception {
String str = "-3.5*(4.5-(4+(-1-1/2)))";
System.out.println("開始計算表示式:"+str);
System.out.println("結果為:"+getResult(str));
}
再來個簡略版的
public class test {
static boolean isNumber(String str) {//判斷表示式是不是隻有一個數字
for(int i=0;i<str.length();i++) {
if(!Character.isDigit(str.charAt(i)) && str.charAt(i)!='.') return false;
}
return true;
}
static Double getResult(String str) {
if(str.isEmpty() || isNumber(str)) {//遞迴頭
return str.isEmpty() ? 0 : Double.parseDouble(str);
}
//遞迴體
if(str.contains(")")) {
int lIndex = str.lastIndexOf("(");//最後一個左括號
int rIndex = str.indexOf(")", lIndex);//對於的右括號
return getResult(str.substring(0,lIndex) + getResult(str.substring(lIndex+1, rIndex)) + str.substring(rIndex+1));
}
if(str.contains("+")) {
int index = str.lastIndexOf("+");
return getResult(str.substring(0,index)) + getResult(str.substring(index+1));
}
if(str.contains("-")) {
int index = str.lastIndexOf("-");
return getResult(str.substring(0,index)) - getResult(str.substring(index+1));
}
if(str.contains("*")) {
int index = str.lastIndexOf("*");
return getResult(str.substring(0,index)) * getResult(str.substring(index+1));
}
if(str.contains("/")) {
int index = str.lastIndexOf("/");
return getResult(str.substring(0,index)) / getResult(str.substring(index+1));
}
return null;//出錯
}
public static void main(String[] args) {
String str = "-3.5+(-1-1)/2";
System.out.println(getResult(str));
}
}
###四、執行結果
五、分析
這種方法以我們平時計算的思路入手,思路較為清晰,程式碼簡略,但直接遍歷拼接字串,程式碼並不好讀,多次遞迴效率也必然較低。引入第二種思路,採用java的容器進一步優化演算法及程式碼。