Мой первоначальный ответ, хотя и решил проблему, был не совсем верным. Вы хотели использовать итеративный подход на первый взгляд (т. Е. Использовать цикл для навигации по исходному массиву и решения всех операций перед возвратом результата).
Итак, я ответил вам:
Обе операции работают, проблема в том, что вы звоните 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);
тогда мы проверяем длину этого массива:
если он содержит только один элемент, он может быть возвращен
в противном случае, если это не так (в нашем случае), мы снова вызываем функцию (на этот раз [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]
));
Добавление этих двух if
блоков в цикл while итеративного подхода сработало бы. Но тогда у вас останется ... рекурсивная функция. Вот почему вы можете начать с рекурсивного подхода. Это позволяет вам легче расширять свой код.
Дополнительная справка по рекурсии