为什么我的计算器功能无法完成数学运算?

时间:2018-07-02 10:16:49

标签: javascript arrays sorting

嗨,所以我很难弄清楚为什么我的函数会进行除法运算,但是将乘法作为数组保留而没有完成数学运算。这是代码:

const mathObj = {
      "*": function(a , b) {return a * b},
      "/": function(a , b) {return a / b},
      "+": function(a , b) {return a + b},
      "-": function(a , b) {return a - b}
    }

    const arr = [ 10, "/" , 2 , "*" , 10 , "/" , 2 ];

    function solveTheMath(arr) {

 
      const len = arr.length;

      for(let i = 0 ; i < len ; i++){
    
          if(arr[i] === "/" || arr[i] === "*"){

            const sliced = arr.slice(i - 1 , i + 2);
     
            var mathResult = mathObj[arr[i]](sliced[0], sliced[2]);
     
            arr.splice(i - 1 , 3, mathResult);
            console.log(arr);
            //[5*5]

        }
      }
    }
    solveTheMath(arr);

为什么乘法不起作用而除法起作用?

1 个答案:

答案 0 :(得分:3)

我的最初答案虽然确实解决了问题,但并不正确。您想通过事物外观使用迭代方法(即使用循环在初始数组中导航并在返回结果之前解决所有操作)。

所以我回复了你

  

两种操作均有效,问题在于您仅调用一次solveTheMath

     

您需要再次调用函数来解决已构造的数组。如果构造的数组仅由一个元素组成,这意味着过程已到达计算的结尾,则可以返回数组的第一个(也是唯一的元素)。

     

您正在以递归方式解决问题:

     

const mathObj = {
  "*": function(a , b) {return a * b},
  "/": function(a , b) {return a / b},
  "+": function(a , b) {return a + b},
  "-": function(a , b) {return a - b}
}

const arr = [ 10, "/" , 2 , "*" , 10 , "/" , 2 ];

function solveTheMath(arr) {


  const len = arr.length;
  
  for(let i = 0 ; i < len ; i++){

      if(arr[i] === "/" || arr[i] === "*"){

        const sliced = arr.slice(i - 1 , i + 2);
        var mathResult = mathObj[arr[i]](sliced[0], sliced[2]);

        arr.splice(i - 1 , 3, mathResult);
        
        if(arr.length == 1) {
          return arr[0];            // <- no more calculations needed, return result
        } else {
          return solveTheMath(arr); // <- more calculations needed, call it again
        };
        

    }
  }
}

console.log(solveTheMath(arr))

但是实际上这是不正确的,您可以使用两种方法:递归和迭代来解决此问题。我最初的回答提供了一个糟糕的解决方案:我保留了for循环,并再次调用了该函数来解决数组中剩余的操作。这不是必需的,因为for循环仅循环查找第二个项目并停止。无论如何,这是一个更清晰的答案,突出了两种方法。

注意:我已将solveTheMath重命名为calculate,并将mathObj重命名为operations


迭代方法

这就是您提出问题时所采用的方法。因为您使用for循环来计算单个函数调用中的所有操作(因此该函数不会一遍又一遍地调用自身)。

为此,我建议使用while循环,因为**修改arr时,您将很难循环(每次循环用一个元素替换三个元素)。

我将以数组[10, "/", 2, "*", 10, "/", 2]作为开始数组,以逐步显示该过程。您可以解决提供的数组的第一个操作。例如,给定:,calculate将在此处计算第一个操作:10, "/", 2

虽然数组包含多个元素,但我们将执行以下操作:

  1. 数组的前三个元素包含:两个因子和一个运算符。通过切片数组,我们可以提取这些值并保存它们。我正在使用destructuring assignment使其更加冗长:

    const [a, operator, b] = arr.slice(0, 3);
    

    此处a = 10operator = "/"b = 2

  2. 我们将在此行中计算此操作的结果:

    const result = operations[operator](a, b);
    

    result = 5(参见:10 / 2

  3. 然后用整数result替换数组的前三个元素:

    arr.splice(0, 3, result);
    

    在这一点上,arr等于[5, "*", 10, "/", 2]

  4. 该块已执行,再次检查while条件。 arr确实包含多个元素,因此该块再次被执行。请记住,此时arr等于[5, "*", 10, "/", 2]等于[10, "/", 2, "*", 10, "/", 2](我们正在计算中取得进展)。

  5. 在第二个循环的结尾,我们有arr等于[50, "/", 2]

  6. 此后等于[25]的循环。

  7. 不再满足while的条件,因为arr仅包含一个元素,while循环已停止并且可以返回结果。

    < / li>

const operations = {
  "*": (a, b) => a * b,
  "/": (a, b) => a / b,
  "+": (a, b) => a + b,
  "-": (a, b) => a - b
}

const calculate = arr => {

  while(arr.length > 1) {  // <- while there are calculations to be done, repeat this block
  
    const [a, operator, b] = arr.slice(0, 3);
    const result = operations[operator](a, b);
  
    arr.splice(0, 3, result);
  
  }

  return arr[0];   // <- no more operations to be done, return result

}

console.log(calculate(
  [10, "/", 2, "*", 10, "/", 2]
));


递归方法

我们可以使用递归方法:该函数将仅计算所提供数组的第一个操作,并以该第一个操作的结果返回一个新数组。

这里是一个例子:

  1. 与迭代数组相同,在给定输入[10, "/", 2, "*", 10, "/", 2]的情况下,我们首先通过对数组进行切片来获取前两个因子和运算符。然后我们将计算操作结果。最后,我们将结果替换为数组的前三个元素:

    const [a, operator, b] = arr.slice(0, 3);
    const result = operations[operator](a, b);
    arr.splice(0, 3, result);
    
  2. 然后我们检查此数组的长度:

    1. 如果仅包含一个元素,则可以返回

    2. 否则,如果没有(在我们的例子中),我们将再次调用该函数(这次在[5, "*", 10, "/", 2]上)。

  3. 因此,该函数以新的输入再次运行,并且arr变为[50, "/", 2],其中包含多个元素,因此需要再次调用该函数(以[50, "/", 2]作为输入)

  4. 现在,arr[25],它仅包含一个元素,可以返回结果(25)。

const operations = {
  "*": (a, b) => a * b,
  "/": (a, b) => a / b,
  "+": (a, b) => a + b,
  "-": (a, b) => a - b
}

const calculate = arr => {

  const [a, operator, b] = arr.slice(0, 3);
  const result = operations[operator](a, b);
  
  arr.splice(0, 3, result);
  
  if (arr.length == 1) {
  
    return arr[0];           // <- no more calculations needed, return result
  
  } else {
  
    return calculate(arr);   // <- more calculations needed, call it again
  
  }

}

console.log(calculate(
  [10, "/", 2, "*", 10, "/", 2]
));


走得更远...

您可以看到两种方法都非常相似:主要过程相同,但是它们处理执行结束的方式不同。在这种情况下,两者都可以合理使用。起初,迭代方法对您来说似乎更自然。但是请记住,递归可以使您解决更复杂的问题。例如,如果您想在函数中实现一种括号系统:

您将如何计算:10*(2+2)/2calculate([10, "*", 2, "+", 2, "/", 2])显然会返回11 ...

取而代之的是输入[[10, "+", 2], "/", 2],这更有意义!我们如何计算正确的结果?

使用我们的递归方法可以很容易地实现:如果a或/和b是数组,那么我们通过在它们上调用calculate来重新分配它们。就是这样:

if(a.constructor == Array) {
  a = calculate(a);
}
if(b.constructor == Array) {
  b = calculate(b);
}

const operations = {
  "*": (a, b) => a * b,
  "/": (a, b) => a / b,
  "+": (a, b) => a + b,
  "-": (a, b) => a - b
}

const calculate = arr => {

  let [a, operator, b] = arr.slice(0, 3);
  if(a.constructor == Array) {
    a = calculate(a);
  }
  if(b.constructor == Array) {
    b = calculate(b);
  }
  
  const result = operations[operator](a, b);
  
  arr.splice(0, 3, result);
  
  if (arr.length == 1) {
  
    return arr[0];           // <- no more calculations needed, return result
  
  } else {
  
    return calculate(arr);   // <- more calculations needed, call it again
  
  }

}

console.log(calculate(
  [[10, "+", 2], "/", 2]
));

在迭代方法的while循环中添加这两个if块会起作用。但是然后您将得到一个递归函数。这就是为什么您可能想直接使用递归方法的原因。这样一来,您可以更轻松地扩展代码。


有关递归的更多参考信息