javascript - 为什么我的计算器功能无法完成数学运算?
问题描述
嗨,所以我无法弄清楚为什么我的函数会进行除法但将乘法保留为数组而不完成数学运算。这是代码:
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);
为什么乘法不起作用,但除法起作用?
解决方案
我最初的答案虽然确实解决了问题,但并不正确。您想从事物的外观上使用迭代方法(即使用循环浏览初始数组并在返回结果之前解决所有操作)。
所以我回复你:
两种操作都有效,问题是您只调用
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
虽然数组包含多个元素,但我们将执行以下操作:
数组的前三个元素包含:两个因子和一个运算符号。通过对数组进行切片,我们可以提取这些值并保存它们。我正在使用解构赋值使其更详细:
const [a, operator, b] = arr.slice(0, 3);
在这里
a = 10
,operator = "/"
并且b = 2
我们将使用此行计算此操作的结果:
const result = operations[operator](a, b);
result = 5
(参见10 / 2
:)然后用整数替换数组的前三个元素
result
:arr.splice(0, 3, result);
此时,
arr
等于[5, "*", 10, "/", 2]
块已执行,
while
再次检查条件。arr
确实包含多个元素,因此再次执行该块。请记住,此时arr
等于[5, "*", 10, "/", 2]
,而不是[10, "/", 2, "*", 10, "/", 2]
(我们正在计算中取得进展)。在第二个循环结束时,我们有
arr
等于[50, "/", 2]
。之后的循环等于
[25]
。while
不再满足条件,因为只arr
包含一个元素,while
循环已停止,可以返回结果。
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]
));
递归方法
我们可以使用递归方法:该函数将只计算所提供数组的第一个操作,并返回一个包含第一个操作结果的新数组。
这是一个例子:
与迭代数组相同,给定输入,
[10, "/", 2, "*", 10, "/", 2]
我们将首先通过对数组进行切片来获取前两个因子和运算符符号。然后我们将计算操作的结果。最后,我们将用这个结果替换数组的前三个元素:const [a, operator, b] = arr.slice(0, 3); const result = operations[operator](a, b); arr.splice(0, 3, result);
然后我们检查这个数组的长度:
如果它只包含一个元素,则可以返回
否则,如果它没有(在我们的例子中)我们再次调用该函数(这次是 on
[5, "*", 10, "/", 2]
)。
因此,该函数使用新输入再次运行并
arr
变为[50, "/", 2]
具有多个元素,因此需要再次调用该函数([50, "/", 2]
作为输入)现在,
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)/2
?calculate([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
块会起作用。但是你会留下一个......递归函数。这就是为什么您可能想直接使用递归方法。这使您可以更轻松地扩展代码。
更多关于递归的参考
- JavaScript 中的递归, freecodecamp.org
- 阶乘的递归实现如何工作,维基媒体
- 了解 JavaScript 中的递归函数,medium.com
- 作用域和函数栈:递归,MDN web docs
推荐阅读
- python - django:如何在每个请求上创建和调用构造函数?
- android - 在 SharedPreferences 类上包装 getter 和 setter
- python - pygi(Gtk3)python中的标签size_request
- git - 这个 git commit 到底发生了什么变化?
- angularjs - Karma-Jasmine 异步测试返回错误:超时 - 在 jasmine.DEFAULT_TIMEOUT_INTERVAL 指定的超时内未调用异步回调
- reactjs - 当状态改变时,元素被复制
- linux - 查找命令结果,不会写入输出
- python - 如何在 Python 中使用 JMX?
- git - 较旧的 git 提交没有 user.name 和 user.email。我该如何改变呢?
- angular - 如何使用angular2中的prime ng将值放在网格表上