Java計(jì)算表達(dá)式的值

輸入:每行輸入一個(gè)表達(dá)式,運(yùn)算符包含+-*/和括號(hào),運(yùn)算符和數(shù)字用空格隔開(kāi)。如果需要擴(kuò)充,其余運(yùn)算符也可以直接再枚舉類中加。
輸入 : 1 * 2 + 3
輸出 : 5
輸入 2 * ( 2 + 3 )
輸出 : 10

本人覺(jué)得自己做得比較好的地方是用枚舉表示運(yùn)算符,同時(shí)對(duì)不合法的表達(dá)式做了異常處理。

代碼如下:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Scanner;
import java.util.Stack;

/**
 * Created by lxhao on 17-6-24.
 */

public class Test {
    public static void main(String args[]) {
        Scanner in = getScanner("input.txt");
        while (in.hasNext()) {
            String[] expression = in.nextLine().split("\\s");
            System.out.println(Claculator.compute(expression));
        }
    }

    //從輸入流讀取輸入數(shù)據(jù)
    public static Scanner getScanner(InputStream is) {
        return new Scanner(is);
    }

    //從文件讀取輸入數(shù)據(jù)
    public static Scanner getScanner(String fileName) {
        try {
            return getScanner(new FileInputStream(fileName));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

/**
 * 表達(dá)式求值
 * <p>
 * 棧的規(guī)則是先進(jìn)后出。利用壓棧的思想來(lái)計(jì)算四則運(yùn)算表達(dá)式是這樣的:我們給定兩個(gè)棧,
 * 一個(gè)用來(lái)存放數(shù)字、一個(gè)用來(lái)存放對(duì)應(yīng)的操作符。假定我們有一個(gè)給定的四則運(yùn)算表達(dá)式a+b+c/d*(e+f)-d*a,
 * 那我們先把這個(gè)表達(dá)式拆分成一個(gè)個(gè)的數(shù)字或者是運(yùn)算符、或者就是括號(hào)了。
 * 然后我們從左至右遍歷每一個(gè)元素,遍歷過(guò)程中遵循步驟和原則如下:
 * (1)遇到數(shù)字則直接壓到數(shù)字棧頂。
 * (2)遇到運(yùn)算符(+-/*)時(shí),若操作符棧為空,則直接放到操作符棧頂,否則,見(jiàn)(3)。
 * (3)若操作符棧頂元素的優(yōu)先級(jí)比當(dāng)前運(yùn)算符的優(yōu)先級(jí)小,則直接壓入棧頂,否則執(zhí)行步驟(4)。
 * (4)彈出數(shù)字棧頂?shù)膬蓚€(gè)數(shù)字并彈出操作符棧頂?shù)倪\(yùn)算符進(jìn)行運(yùn)算,把運(yùn)算結(jié)果壓入數(shù)字棧頂,重復(fù)(2)和(3)直到當(dāng)前運(yùn)算符被壓入操作符棧頂。
 * (5)遇到左括號(hào)“(”時(shí)則直接壓入操作符棧頂。
 * (6)遇到右括號(hào)“)”時(shí)則依次彈出操作符棧頂?shù)倪\(yùn)算符運(yùn)算數(shù)字棧的最頂上兩個(gè)數(shù)字,直到彈出的操作符為左括號(hào)。
 * <p>
 * <p>
 * http://elim.iteye.com/blog/1981197
 */

class Claculator {

    public static double compute(String[] expression) {
        //操作數(shù)棧
        Stack<Double> numberStack = new Stack<>();
        //表達(dá)式棧
        Stack<Operator> operatorStack = new Stack<>();
        for (String s : expression) {
            //數(shù)字直接入棧
            if (Operator.getOperator(s) == Operator.OTHER) {
                numberStack.push(Double.parseDouble(s));
                continue;
            }
            Operator operator = Operator.getOperator(s);
            //棧為空或者遇到左括號(hào)直接入棧
            if (operatorStack.isEmpty() || operator == Operator.LEFT_BRACKET) {
                operatorStack.push(operator);
                continue;
            }
            //遇到右括號(hào)則一直計(jì)算到左括號(hào)
            if (operator == Operator.RIGHT_BRACKET) {
                while (operatorStack.peek() != Operator.LEFT_BRACKET && numberStack.size() >= 2) {
                    //因?yàn)楹竺娴臄?shù)會(huì)先彈出來(lái),除法和減法是區(qū)分先后順序的(比如1 / 2不能算成2 / 1)
                    double n1 = numberStack.pop();
                    double resTmp = operatorStack.pop().compute(numberStack.pop(), n1);
                    numberStack.push(resTmp);
                }
                //不合法的表達(dá)式
                if (operatorStack.pop() != Operator.LEFT_BRACKET) {
                    throw new IllegalArgumentException();
                }
                continue;
            }
            if (operator.getPriority() <= operatorStack.peek().getPriority()) {
                //不合法的表達(dá)式
                if (numberStack.size() < 2) {
                    throw new IllegalArgumentException();
                }
                while (operatorStack.size() > 0 && numberStack.size() >= 2 && operator.getPriority() <= operatorStack.peek().getPriority()) {
                    //彈出運(yùn)算符和兩個(gè)數(shù)組進(jìn)行計(jì)算
                    double n1 = numberStack.pop();
                    double resTmp = operatorStack.pop().compute(numberStack.pop(), n1);
                    numberStack.push(resTmp);
                }
                operatorStack.push(operator);
            } else {
                //當(dāng)前運(yùn)算符的優(yōu)先級(jí)比較高
                operatorStack.push(operator);
            }
        }
        while (operatorStack.size() > 0 && numberStack.size() >= 2) {
            double n1 = numberStack.pop();
            double resTmp = operatorStack.pop().compute(numberStack.pop(), n1);
            numberStack.push(resTmp);
        }
        if (numberStack.size() == 1 && operatorStack.isEmpty()) {
            return numberStack.pop();
        }
        //不合法的表達(dá)式
        throw new IllegalArgumentException();
    }
}

/**
 * 運(yùn)算符
 */

enum Operator {
    PLUS("+", 1) {
        @Override
        public double compute(double num1, double num2) {
            return num1 + num2;
        }
    },
    MINUS("-", 1) {
        @Override
        public double compute(double num1, double num2) {
            return num1 - num2;
        }
    },
    MULTIPLY("*", 2) {
        @Override
        public double compute(double num1, double num2) {
            return num1 * num2;
        }
    },
    DIVIDE("/", 2) {
        @Override
        public double compute(double num1, double num2) {
            return num1 / num2;
        }
    },
    REMAINDER("%", 2) {
        @Override
        public double compute(double num1, double num2) {
            return num1 % num2;
        }
    },
    EXPONENTIATION("^", 3) {
        @Override
        public double compute(double num1, double num2) {
            return Math.pow(num1, num2);
        }
    },
    INTEGER_DIVISION("http://", 2) {
        @Override
        public double compute(double num1, double num2) {
            return Math.floor(num1 / num2);
        }
    },
    RIGHT_BRACKET(")", 0) {
        @Override
        public double compute(double num1, double num2) {
            throw new UnsupportedOperationException();
        }
    },
    LEFT_BRACKET("(", 0) {
        @Override
        public double compute(double num1, double num2) {
            throw new UnsupportedOperationException();
        }
    },
    OTHER("", 0) {
        @Override
        public double compute(double num1, double num2) {
            return 0;
        }
    };

    private String value;
    private int priority;

    private Operator(String value, int priority) {
        this.value = value;
        this.priority = priority;
    }

    public static Operator getOperator(String value) {
        for (Operator operator : Operator.values()) {
            if (operator.value.equals(value)) {
                return operator;
            }
        }
        return OTHER;
    }

    public int getPriority() {
        return priority;
    }

    public abstract double compute(double num1, double num2);
}

input.txt的測(cè)試數(shù)據(jù):

2 + 8 - 9 * ( 12 / 6 / 2 / 2 ) + 2 * 2 + 2 ^ 2 + 2 + 8 - 9 * ( 12 / 6 ) + 2 * 2 + 2 ^ 2
2 + 3 * 3 * 3 + 3 * 3 * 3
2 + 2 + 3 * 9
2 - 18 - 19
1 + 7 ^ 2
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,285評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,548評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,878評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,098評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,361評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,613評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,339評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容