js achieve - Reverse Polish Notation

There is no way, unless you're talented.
- Elliott


js achieve - Reverse Polish Notation

2019-05-26 by liberal arts students

Recently compiler principle experiment involving the Reverse Polish Notation, and heard someone asked Reverse Polish Notation algorithms front end of the interview process to achieve, before discrete mathematics curriculum also involves Reverse Polish Notation, as a front-end staff, and finally press lost and want to achieve js seek reverse Polish Notation algorithms. I went through a lot of information, found that although some of the basic algorithm to achieve the corresponding function, but slightly wrong in the details of the deal; and some algorithm written too complex, you want to fully understand the costly and code not because of a bloated complex algorithm itself, but added some operators, to understand the nature of this algorithm is unfavorable. So I simply their own use js to write again this algorithm, the purpose is to organize your thoughts, showing the nature of the algorithm to the bit front-end enthusiasts, I hope you interview well! This article will achieve:

  • Ordinary arithmetic expressions into Reverse Polish Notation
  • Polish inversion of values

You can go on github Figure demo download

First, the definition of Reverse Polish Notation

EDITORIAL the operands, and the operational sign written on the back. In this method of representation showing expression also called postfix. Reverse Polish order is characterized by constant operand, the arithmetic sign reflecting the position of the operation sequence.

Second, the algorithm description

According to ordinary arithmetic expression evaluates Reverse Polish Notation:

  1. First, an operator stack configuration, this operator to follow the principle of the higher priority stack in the stack.
  2. Read a simple arithmetic expression represented by infix, for convenience, it is assumed that more than simple arithmetic expressions right plus the lowest priority of the special symbol "#."
  3. From left to right scan the arithmetic expression is determined from a start character, if the character is a number, number analysis to the end of the string and the string directly to a digital output.
  4. If the number is not, the character is the operator, comparing this time to be the priority relationship.
    Approach is as follows: The operator precedence relationships characters and the operator stack compared. If the character is higher than the priority relationship of this operator stack operator, the operator stack. If not, then this operator operator stack pop from the stack, the stack characters.
  5. Repeat until the complete scanning simple arithmetic expressions, identify all the characters are handled correctly, we will be able to be represented in infix style of simple arithmetic expressions into simple arithmetic expressions Reverse Polish representation.

Polish calculate the inverse value of the expression:

  1. A stack configuration, storage operand.
  2. Read a simple arithmetic expression represented by the Reverse Polish Notation.
  3. Scan from left to right determines the simple arithmetic expression and the character, if the character is an operand, then the character stack. If the operator, if the operator is a binary operator, then the top of the stack of two operands is the arithmetic operation result stack, and execution of the two operands from the stack pop operation.
  4. Repeat until the complete scanning simple arithmetic expression Reverse Polish Notation, make sure that all the characters are handled correctly, we can find the value of the RPN arithmetic expressions.

Third, the core code

// 适用于无符整数四则运算, 但运算结果可能是负数,如减法
(function () {
  'use strict'
  const rpn = {
    _precedence: {'/': 2, '*': 2, '-': 1, '+': 1, '#': 0},
    
    /**
     * operations
     * @private
     */
    _operation: {
      '+': (a, b) => (+a) + (+b),
      '-': (a, b) => (+a) - (+b),
      '*': (a, b) => (+a) * (+b),
      '/': (a, b) => (+a) / (+b)
    },

    /**
     * split expression to array
     * @private
     * @param exp - infix expression
     * @returns {Array|null}
     */
    _splitExp: function (exp) {
      return exp.match(/\d+|[^\d\s\t]/g);
    },

    /**
     * check a character, is or not an operator
     * @private
     * @param char - character
     * @return {boolean}
     */
    _isOperator: function (char) {
      return /^[\/\*\-\+#]$/.test(char);
    },

    /**
     * check character, is or not a bracket
     * @private
     * @param char - character
     * @retuens {boolean}
     */
    _isBracket: function (char) {
      return /^[\(\)]$/.test(char);
    },

    /**
     * check string, is or not a number
     * @private
     * @param str - character
     * @returns {boolean}
     */
    _isNumber: function (str) {
      return /^\d+$/.test(str);
    },

    /**
     * check exp, is or not a valid expression
     * @param {string} exp - expression 
     * @returns {boolean} - 
     */
    _isValidExpression: function (exp) { // 含有除数字、括号、操作符以外的符号即为非法
      return !/[^\d\s\t\+\-\*\/\(\)]/.test(exp);
    },

    /**
     * transfer infix expression to reverse polish notation
     * @param exp - infix express
     * @returns {string|null}
     */
    infix2rpn: function(exp) {
      if (!rpn._isValidExpression(exp)) return null;  // 用于保证以下处理的是合法的表达式

      var arrExp = rpn._splitExp(exp);  // 输入串分割
      var opStack = [];                 // 运算符栈
      var rpnStack = [];                // 存放逆波兰式结果
      
      arrExp = arrExp.concat('#');      // 加入最低优先级的算符 '#'
      
      var i,                        // 用于遍历arrExp
          item,                     // 遍历arrExp时暂存
          op,                       // 暂存opStack中的操作符
          len = arrExp.length;      // 记录arrExp长度
      for (i = 0; i < len; i ++) {
        item = arrExp[i];
        if (rpn._isNumber(item)) {
          rpnStack.push(item);
        } else if (rpn._isOperator(item)) {  
          while (opStack.length) {
            op = opStack[opStack.length-1];        // push性能低于pop和数组按索引取值,要尽量避免push
            if(op === '(') {                // 栈顶运算符是左括号,需单独处理
              break;
            } else if (rpn._precedence[item] > rpn._precedence[op]) { // 否则,栈顶是运算符。并且如果...
              // 当前算符优先级大于算符栈栈顶优先级
              break;
            } else {                    // 当前算符优先级小于等于算符栈栈顶优先级
              rpnStack.push(opStack.pop()); // 弹出算符栈栈顶算符并放入逆波兰式结果栈中
            }
          }
          opStack.push(item);           // 将运算符压入
        } else {                        // item是括号
          if (item === '(') {           // 是 '('
            opStack.push(item);
          } else  {  // 否则,item是 ')'
            while (opStack[opStack.length-1] !== '(') {
              rpnStack.push(opStack.pop());
            }                   // ')' 遇 '(' ,相抵消
            opStack.pop();
          }
        }
      } 
      return rpnStack.length ? rpnStack.join(' ') : null;
    },


    /**
     * calculate reverse polish notation - 本函数目前只支持二元运算
     * @param exp - reversed polish notation
     * @returns {number|null}
     */
    rpnCalculate: function (exp) {
      if (!rpn._isValidExpression(exp)) return null;  // 用于保证以下处理的是合法的表达式

      var arrExp = rpn._splitExp(exp);
      var calcStack = [];
      var item;                       // in arrExp
      var param1, param2;           // 运算对象

      var i, len = arrExp.length;
      for (i = 0; i < len; i ++) {
        item = arrExp[i];
        if (rpn._isNumber(item)) {
          calcStack.push(+item);    // 先将item转换为数值再压栈
        } else {                    // 否则item就是运算符
          param2 = calcStack.pop();
          param1 = calcStack.pop();
          calcStack.push(rpn._operation[item](param1, param2));// 执行运算并将结果压栈
        }
      }  
      return calcStack.pop();
    },

    /**
     * calculate expression
     * @param exp - expression string
     * @returns {number|null}
     */
    calculate: function (exp) {
      return rpn.rpnCalculate(rpn.infix2rpn(exp));
    }
  }
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = rpn;
  }

  if (typeof window !== 'undefined') {
    window.rpn = rpn;
  }
}());

IV Summary

  1. The above code only supports four operations to achieve, because I just want to present the essence of this algorithm, if you want in-depth, then we must consider issues such as the characteristics of the operator (unary, binary operators, etc.), so that it becomes paper hard to read.
  2. Note that I did not put the left and right parenthesis as a operator, although the left bracket pressed into the stack, but a separate approach for the left and right parentheses.
  3. Due to my limited knowledge, it is inevitable mistakes, criticism welcome you guidance.

V. Resources

javascript: formula calculation result expression Reverse Polish

JavaScript infix expressions into Reverse Polish Notation (four operations)

Polish, and inverse Polish expression evaluation

Reverse Polish Notation Tools

Guess you like

Origin www.cnblogs.com/wen-k-s/p/10925987.html