1. 程式人生 > >java棧實現簡單的計算器

java棧實現簡單的計算器

一、程式碼

直接上程式碼,整個程式碼分為兩個類calc.StackCalculator.java,calc.Calculator.java
1、StackCalculator.java

/**
 * 用棧實現表示式的運算v1.0
 * 支援運算:+、-、*、/、%、^、!、()
 * 輸入的表示式需以"#"結束
 */
package calc;

import java.util.Stack;

public class StackCalculator {
    private Stack<Character> optr = new Stack<Character>();// 操作符棧
private Stack<Float> opnd = new Stack<Float>();// 運算元棧 private final Character END_Character = '#';// 輸入表示式的結束字元 public final int ADD = 0, SUB = 1, MUL = 2, DIV = 3, MOD = 4, POW = 5, FAC = 6, L_P = 7, R_P = 8, EOF = 9; public final char[][] PRI = {//運算子優先等級 [棧頂]'levle'[當前] // |----------------當前運算子----------------|
// + - * / % ^ ! ( ) #(結束字元) /* + */ {'>','>','<','<','<','<','<','<','>','>'}, /* - */ {'>','>','<','<','<','<','<','<','>','>'}, /* 棧 * */ {'>','>','>'
,'>','>','<','<','<','>','>'}, /* 頂 / */ {'>','>','>','>','>','<','<','<','>','>'}, /* 運 % */ {'>','>','>','>','>','<','<','<','>','>'}, /* 算 ^ */ {'>','>','>','>','>','>','<','<','>','>'}, /* 符 ! */ {'>','>','>','>','>','>','>',' ','>','>'}, /* ( */ {'<','<','<','<','<','<','<','<','=',' '}, /* ) */ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /* # */ {'<','<','<','<','<','<','<','<',' ','='} }; /** * 中綴表示式計算,程式核心部分 * @param expression * 輸入的有效中綴表示式 * @param RPN * 輸出的逆波蘭表示式(Reverse Polish Notation) * @return */ public float calcExpression(String expression, StringBuffer RPN) throws Exception { // 初始時操作符棧壓入開始識別符號,與結束字元對應 optr.push(END_Character); // 剔除表示式中的空格 expression = removeSpace(expression); int i = 0;// 表示讀取表示式的位置 while (!optr.empty()) { char currenSymbol = expression.charAt(i);// 當前掃描到的表示式符號 if (Character.isDigit(currenSymbol)) { i = readNumber(expression, i, opnd);// 讀入(可能多位的)運算元 RPN.append(opnd.peek() + " ");// 計入逆波蘭表示式 } else { switch (orderBetween(optr.peek(), currenSymbol)) { case '>':// 棧頂運算子的優先順序大於當前運算子,則1.取出操作符棧頂運算子,2.取出資料棧中的一個或多個數據(取決於運算子的型別),3.運算並把結果壓入資料棧中 char op = optr.pop();// 取出棧頂操作符 RPN.append(op + " ");// 當操作符可以計算時計入逆波蘭表示式,與逆波蘭表示式的計算過程恰好吻合 Calculator ca = new Calculator();// 基本計算操作物件 if ('!' == op) {// 一元運算子的計算 float number = opnd.pop();// 取出運算元棧頂數值 System.out.println("計算過程:" + "[" + number + "]" + "" + op + "=" + "[" +ca.calcu(number, op) +"]" ); opnd.push(ca.calcu(number, op));// 計算並將結果入棧 } else {// 二元運算子的計算 float number2 = opnd.pop();// 取出運算元棧頂數值 float number1 = opnd.pop();// 取出運算元棧頂數值 System.out.println("計算過程:" + "[" + number1 + "]" + "" + op + "" + "[" + number2 + "]" + "=" + "[" +ca.calcu(number1, op, number2) + "]"); opnd.push(ca.calcu(number1, op, number2));// 計算並將結果入棧 } break; case '<':// 棧頂運算子的優先順序小於當前運算子,計算推遲,當前運算子入棧 optr.push(currenSymbol); i++; break; case '=':// 棧頂運算子的優先順序等於當前運算子,脫括號並接收下一個字元 optr.pop(); i++; break; case ' ': throw new Exception("ERROR"); } } } return opnd.pop();// 運算元棧的最後一個元素即為需要的結果 } /** * 只關注於求中綴表示式的介面 * @param expression * @return * @throws Exception */ public float calcExpression(String expression) throws Exception { return calcExpression(expression, new StringBuffer()); } /** * 對逆波蘭表示式進行計算 * @param rpn * @return * @throws Exception */ public float calcExpressionRpn(String rpn) throws Exception{ String[] chs = rpn.split(" "); int i = 0; int chLength = chs.length; while(i < chLength) { Calculator ca = new Calculator();// 基本計算操作物件 if( convertStrToDigit(chs[i]) != null ) {// 運算元直接入棧 opnd.push(convertStrToDigit(chs[i])); } else { char op = chs[i].charAt(0); if("!".equals( chs[i] )) { float number = opnd.pop(); opnd.push(ca.calcu(number, op)); System.out.println("計算過程:" + "[" + number + "]" + "" + op + "=" + "[" +ca.calcu(number, op) +"]" ); } else { float number2 = opnd.pop();// 取出運算元棧頂數值 float number1 = opnd.pop();// 取出運算元棧頂數值 System.out.println("計算過程:" + "[" + number1 + "]" + "" + op + "" + "[" + number2 + "]" + "=" + "[" +ca.calcu(number1, op, number2) + "]"); opnd.push(ca.calcu(number1, op, number2));// 計算並將結果入棧 } } i++; } return opnd.pop(); } /** * 將字串轉化為浮點數 * @param str * @return */ private Float convertStrToDigit(String str) { try{ float num = Float.valueOf(str); return num; } catch(Exception e){ return null; } } /** * 將char型的數字字元轉為浮點型資料 * @param ch * @return */ private float CharToFloat(char ch) { return Float.valueOf("" + ch); } /** * 將起始為i的子串解析為數值,並存入運算元棧中 * @param expression 表示式 * @param i 開始解析的位置 * @param stk 將解析完畢的數值存入此棧中 * @return 該數值解析完畢後,返回表示式需要解析的下一個位置 * @throws Exception 解析錯誤 */ private int readNumber(String expression, int i, Stack<Float> stk) throws Exception { stk.push(CharToFloat(expression.charAt(i++)));// 當前數位對應的數值進棧 char op = expression.charAt(i); // 讀取下一個字元 while (Character.isDigit(op)) {// 只要後續還有緊鄰的數字 stk.push(stk.pop() * 10 + CharToFloat(op));// 彈出原運算元並追加新數位後,新數值重新入棧 op = expression.charAt(++i);// 下一個字元 } if ('.' != op) return i;// 如果最後一個數字後面不是小數點,說明解析完成,則返回當前位置 op = expression.charAt(++i); float fraction = 1; while (Character.isDigit(op)) { stk.push(stk.pop() + CharToFloat(op) * (fraction /= 10)); op = expression.charAt(++i); } if ('.' == op) throw new Exception("ERROR");// 如果還有小數點則錯誤 return i;// 返回當前解析字元的位置 } /** * 根據運算子獲取在優先順序表中的秩(RANK) * @param op * @return * @throws Exception */ private int getOperRank(char op) throws Exception { switch (op) { case '+': return ADD; case '-': return SUB; case '*': return MUL; case '/': return DIV; case '%': return MOD; case '^': return POW; case '!': return FAC; case '(': return L_P; case ')': return R_P; case '#': return EOF; default: throw new Exception("ERROR"); } } /** * 比較棧頂和當前字元的優先順序 * @param peekOptr 棧頂運算子 * @param currenOptr 當前掃描到的運算子 * @return 優先順序表中的資料項 * @throws Exception */ private Character orderBetween(Character peekOptr, char currenOptr) throws Exception { return PRI[getOperRank(peekOptr)][getOperRank(currenOptr)]; } /** * 剔除字串之間的空格 * @param str * @return */ public String removeSpace(String str) { char[] chs = str.toCharArray(); StringBuffer newStr = new StringBuffer(); int i = 0; while (i < str.length()) { if (' ' != chs[i]) { newStr.append(chs[i]); } i++; } return newStr.toString(); } // test public static void main(String[] args) { StackCalculator sc = new StackCalculator(); String s = "(1 + 2^3!-4)*(5!-(6-( 7-(89-0!))))#";// 2013 StringBuffer rpn = new StringBuffer(); try { System.out.println("結果:" + sc.calcExpression(s, rpn)); System.out.println("逆波蘭表示式:" + rpn); System.out.println("\n計算逆波蘭表示式:"); sc.calcExpressionRpn(rpn.toString()); } catch (Exception e) { e.printStackTrace(); } } }

2、Calculator.java

package calc;

public class Calculator {
    /**
     * 一元運算子
     * 
     * @param a
     *            運算元
     * @param op
     *            操作符
     * @return 結果浮點型
     */
    public float calcu(float n, char op) throws Exception {
        return fact(n);// 這裡只有階乘一個一元運算子
    }

    /**
     * 二元運算子
     * 
     * @param a
     *            運算元1
     * @param op
     *            操作符
     * @param b
     *            運算元2
     * @return 結果浮點型
     * @throws Exception
     */
    public float calcu(float a, char op, float b) throws Exception {
        switch (op) {
        case '+':
            return a + b;
        case '-':
            return a - b;
        case '*':
            return a * b;
        case '/':
            return div(a, b);
        case '%':
            return mod(a, b);
        case '^':
            return (float) Math.pow(a, b);
        default:
            throw new Exception("ERROR");
        }
    }

    private float div(float a, float b) throws Exception {
        if (b == 0)
            throw new Exception("除數不能為0!");
        return a / b;
    }

    // 取餘
    private float mod(float a, float b) throws Exception {
        if (b == 0)
            throw new Exception("除數不能為0!");
        return a % b;
    }

    // 階乘 n!(n<=20)
    private float fact(float n) throws Exception {
        if (n < 0)
            throw new Exception("階乘運算數不能為負數!");
        if (n > 34)
            throw new Exception("階乘數不能超過34,否則越界!");// 可以考慮改進以使計算更大數值的階乘

        if (n == 0)
            return 1;// 0!=1
        int num = (int) n;
        if (num == n) {// n為整數
            float result = 1;
            while (num > 0) {
                result *= num--;
            }
            return result;
        } else
            throw new Exception("階乘運算數必須為整數!");
    }

}

二、視訊資源

如果想要弄明白關於表示式求值的實現細節和邏輯,建議參考鄧俊輝的資料結構MOOC課程(b站上有),下面給出專門講解表示式求值的視訊的百度雲連結:https://pan.baidu.com/s/1vlkCpHTVjvzSalgrewgYoA 密碼:ixo6