1. 程式人生 > >(演算法)java完成解析數學算式(計算器)一 —— 遞迴、正則直接遍歷字串解析

(演算法)java完成解析數學算式(計算器)一 —— 遞迴、正則直接遍歷字串解析

一、程式要求

解析一般數學算式,實現簡單的帶括號的加減乘除運算。

二、基本思路

先從我們人的角度,考慮平時在計算一個式子的思路,任意假設一個的數學表示式-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的容器進一步優化演算法及程式碼。