Browse Source

逆波兰法四则运算

MaiXinRong 4 years ago
parent
commit
0686af0709
1 changed files with 152 additions and 1 deletions
  1. 152 1
      app/public/js/zh_calc.js

+ 152 - 1
app/public/js/zh_calc.js

@@ -122,5 +122,156 @@ const ZhCalc = (function () {
         return value ? new Decimal(value).toDecimalPlaces(decimal).toNumber() : null;
     }
 
-    return {add, sub, mul, div, round, isNonZero: zhBaseCalc.isNonZero}
+    const ExprCalc = {
+        /**
+         * 判断value是否是四则运算符
+         * @param {String} value - 判断字符串
+         * @return {Boolean}
+         */
+        isOperator(value) {
+            const operatorString = "+-*/()";
+            return operatorString.indexOf(value) > -1;
+        },
+        /**
+         * Rpn解析栈
+         * @param exp
+         * @returns {Array}
+         */
+        parse2Rpn(exp) {
+            const getPriority = function (value){
+                switch(value){
+                    case '+':
+                    case '-':
+                        return 1;
+                    case '*':
+                    case '/':
+                        return 2;
+                    default:
+                        return 0;
+                }
+            };
+            const priority = function (o1, o2){
+                return getPriority(o1) <= getPriority(o2);
+            };
+
+            const inputStack = [];
+            const outputStack = [];
+            const outputQueue = [];
+
+            for (let i = 0, len = exp.length; i < len; i++) {
+                const cur = exp[i];
+                if (cur !== ' ' ) {
+                    inputStack.push(cur);
+                }
+            }
+            let num = '', isNumPre = false, isOperatorPre = false;
+            while(inputStack.length > 0){
+                const cur = inputStack.shift();
+                if (this.isOperator(cur) && !(cur === '-' && !isNumPre)) {
+                    if (isNumPre) {
+                        outputQueue.push(parseFloat(num));
+                        num = '';
+                    }
+                    if (cur === '(') {
+                        outputStack.push(cur);
+                    } else if (cur === ')') {
+                        let po = outputStack.pop();
+                        while (po !== '(' && outputStack.length > 0) {
+                            outputQueue.push(po);
+                            po = outputStack.pop();
+                        }
+                        if (po !== '(') {
+                            throw "error: unmatched ()";
+                        }
+                    } else {
+                        while(priority(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0){
+                            outputQueue.push(outputStack.pop());
+                        }
+                        outputStack.push(cur);
+                    }
+                    isNumPre = false;
+                    isOperatorPre = true;
+                } else {
+                    num = num + cur;
+                    isNumPre = true;
+                    isOperatorPre = false;
+                    if (inputStack.length === 0) {
+                        outputQueue.push(parseFloat(num));
+                    }
+                }
+            }
+            if (outputStack.length > 0) {
+                if (outputStack[outputStack.length - 1] === ')' || outputStack[outputStack.length - 1] === '(') {
+                    throw "error: unmatched ()";
+                }
+                while (outputStack.length > 0) {
+                    outputQueue.push(outputStack.pop());
+                }
+            }
+            return outputQueue;
+        },
+        /**
+         * 计算Rpn解析栈
+         * @param rpnQueue
+         * @returns {*}
+         */
+        evalRpn(rpnQueue) {
+            const getResult = function (num1, num2, opera) {
+                switch (opera) {
+                    case '+':
+                        return add(num1, num1);
+                    case '-':
+                        return sub(num1, num2);
+                    case '*':
+                        return mul(num1, num2, 10);
+                    case '/':
+                        return div(num1, num2, 10);
+                    default:
+                        throw '参数错误';
+                }
+            };
+            const outputStack = [];
+            while(rpnQueue.length > 0){
+                const cur = rpnQueue.shift();
+
+                if (!this.isOperator(cur)) {
+                    outputStack.push(cur);
+                } else {
+                    if (outputStack.length < 2) {
+                        throw "unvalid stack length";
+                    }
+                    const sec = outputStack.pop();
+                    const fir = outputStack.pop();
+
+                    outputStack.push(getResult(fir, sec, cur));
+                }
+            }
+
+            if (outputStack.length !== 1) {
+                throw "unvalid expression";
+            } else {
+                return outputStack[0];
+            }
+        },
+        /**
+         * 解析四则运算字符串并计算
+         * @param {String} expr
+         * @returns
+         */
+        calcExprStrRpn(expr) {
+            try {
+                if (!isNaN(Number(expr))) {
+                    return Number(expr);
+                } else {
+                    const rpnArr = this.parse2Rpn(expr);
+                    const result = this.evalRpn(rpnArr);
+                    return result === Infinity ? null : result;
+                }
+            } catch (err) {
+                return null;
+            }
+        },
+    };
+
+    return {add, sub, mul, div, round, isNonZero: zhBaseCalc.isNonZero, calcExpr: ExprCalc.calcExprStrRpn}
 })();