JavaScript — write a function that can solve a math [removed]without eval)

后端 未结 2 1048
礼貌的吻别
礼貌的吻别 2020-12-21 06:45

Ultimately I want to take this:

2x + 3 = 5

and solve for x, by first subtract 3 from both sides so 2x = 2, then divide both si

相关标签:
2条回答
  • 2020-12-21 07:15

    While your problem doesn't require to construct, binary expression tree is a good way to brainstorm the logic to solve a math query.

    So for the query 3 - 6 * 3 / 9 + 5, the representative binary expression tree is:

    plus
      |_minus
      | |_3
      | |_divide
      |   |_times
      |   | |_3
      |   | |_6
      |   |_9
      |_5
    

    to solve above tree, you recursively solve from the leaf level up to the root.

    Again, you don't need to construct a tree. It just helps us to see the logic of parsing here:

    • Get the last minus or plus expression in query and solve left and right child of that expression.
    • If no plus/minus, get the last times/division expression and solve left and right child
    • If meet a number, return that number value.

    Given above logic, here is an implementation:

    function solve(str) {
      var expressionIndex = Math.max(str.lastIndexOf("-"), str.lastIndexOf("+"));
      if (expressionIndex === -1) {
        expressionIndex = Math.max(str.lastIndexOf("*"), str.lastIndexOf("/"));
      }
      if (expressionIndex === -1) {
        var num = Number.parseInt(str.trim());
        if (isNaN(num)) {
          throw Exception("not a valid number");
        } else {
          return num;
        }
      } else {
        var leftVal = solve(str.substring(0, expressionIndex).trim());
        var rightVal = solve(str.substring(expressionIndex + 1).trim());
        switch (str[expressionIndex]) {
          case "+":
            return leftVal + rightVal;
          case "-":
            return leftVal - rightVal;
          case "*":
            return leftVal * rightVal;
          case "/":
            return leftVal / rightVal;
        }
      }
    }
    
    function parse(str) {
      var expressionIndex = Math.max(str.lastIndexOf("-"), str.lastIndexOf("+"));
      if (expressionIndex === -1) {
        expressionIndex = Math.max(str.lastIndexOf("*"), str.lastIndexOf("/"));
      }
      if (expressionIndex === -1) {
        var num = Number.parseInt(str.trim());
        if (isNaN(num)) {
          throw Exception("not a valid number");
        } else {
          return { type: "number", value: num };
        }
      } else {
        var leftNode = parse(str.substring(0, expressionIndex).trim());
        var rightNode = parse(str.substring(expressionIndex + 1).trim());
        return {
          type: "expression",
          value: str[expressionIndex],
          left: leftNode,
          right: rightNode
        };
      }
    }
    
    console.log(solve("3 - 6 * 3 / 9 + 5"));
    console.log(parse("3 - 6 * 3 / 9 + 5"));

    Above is a solution for very simple query with only +, -, *, / (no parenthesis, e.g.). For solving a equation like your first example requires a lot more of work.

    EDIT: add a parse function to return the tree.

    0 讨论(0)
  • 2020-12-21 07:32

    You can do that in following steps:

    • First of all use split() and split by the + and - which will occur after multiplication and division.
    • Then use map() on array and split() it again by * and /.
    • Now we have a function which will which will evaluate an array of numbers with operators to single number.
    • Pass the nested array to complete multiplication and division.
    • Then pass that result again to sovleSingle and perform addition and subtraction.

    The function works same as eval as long as there are no brackets ().

    Note: This doesnot matters the which occurs first among + and - or which occurs first among * and /. But *,/ should occur before +,-

    function solveSingle(arr){
      arr = arr.slice();
      while(arr.length-1){
        if(arr[1] === '*') arr[0] = arr[0] * arr[2]
        if(arr[1] === '-') arr[0] = arr[0] - arr[2]
        if(arr[1] === '+') arr[0] = +arr[0] + (+arr[2])
        if(arr[1] === '/') arr[0] = arr[0] / arr[2]
        arr.splice(1,1);
        arr.splice(1,1);
      }
      return arr[0];
    }
    
    function solve(eq) {
      let res = eq.split(/(\+|-)/g).map(x => x.trim().split(/(\*|\/)/g).map(a => a.trim()));
      res = res.map(x => solveSingle(x)); //evaluating nested * and  / operations.
       
      return solveSingle(res) //at last evaluating + and -
      
      
    }
    
    console.log(solve("3 - 6 * 3 / 9 + 5")); //6
    console.log(eval("3 - 6 * 3 / 9 + 5")) //6

    0 讨论(0)
提交回复
热议问题