同时在 JavaScript 中一遍又一遍地循环



我正在编写计算器代码,考虑操作的优先级。 我正在尝试做的是替换与我编写的正则表达式匹配的优先级并计算它,并循环遍历它,直到字符串的长度(最初是输入(变为 1。

下面是我的代码,但运行它会导致无限循环。

const Calculator = function() {
this.evaluate = string => {
let regex = /((d+)s[*/+-]s(d+))|(d+)s[*/]s(d+)|(d+)s[+-]s(d+)/g
while(string.length > 1) {
string.replace(regex, cal);
}
return string;
}
};
function cal(argument) {
let temp = argument.split(' ');
let regexP = /((d+)|(d+))/g
if(temp[0].match(regexP)) {
temp[0] = temp[0].slice(1);
temp[2] = temp[2].slice(0, 1);
}
switch(temp[1]) {
case '+': return +temp[0] + +temp[2];
case '-': return +temp[0] - +temp[2];
case '*': return +temp[0] * +temp[2];
case '/': return +temp[0] / +temp[2];
default: return undefined;
}
}
let calculate = new Calculator()
console.log(calculate.evaluate("2 / 2 + 3 * 4 - 6"));

出于某种原因,代码一遍又一遍地循环并且不返回值。

我做错了什么,我该如何解决?

你需要

(1( 将调用.replace的结果分配给字符串(Javascript 字符串是不可变的(:

string.replace(regex, cal);

(2(生成的字符串可以完成所有+-*/操作,但长度仍然大于1,例如,如果结果3 * 4导致12。请改用while(/[-+*/]/.test(string)) {

const Calculator = function() {
this.evaluate = string => {
let regex = /((d+)s[*/+-]s(d+))|(d+)s[*/]s(d+)|(d+)s[+-]s(d+)/g
while(/[-+*/]/.test(string)) {
string = string.replace(regex, cal);
}
return string;
}
};
function cal(argument) {
let temp = argument.split(' ');
let regexP = /((d+)|(d+))/g
if(temp[0].match(regexP)) {
temp[0] = temp[0].slice(1);
temp[2] = temp[2].slice(0, 1);
}
switch(temp[1]) {
case '+': return +temp[0] + +temp[2];
case '-': return +temp[0] - +temp[2];
case '*': return +temp[0] * +temp[2];
case '/': return +temp[0] / +temp[2];
default: return undefined;
}
}
let calculate = new Calculator()
console.log(calculate.evaluate("2 / 2 + 3 * 4 - 6"));

您还可以通过一次性匹配和解构左数字、运算符和右数字来改进cal中的代码,使用

`const [, left, operator, right] = matchedSubstr.match(/(d+) ([-+*/]) (d+)/);

const Calculator = function() {
this.evaluate = string => {
let regex = /((d+)s[*/+-]s(d+))|(d+)s[*/]s(d+)|(d+)s[+-]s(d+)/g
while(/[-+*/]/.test(string)) {
string = string.replace(regex, cal);
}
return string;
}
};
function cal(matchedSubstr) {
const [, left, operator, right] = matchedSubstr.match(/(d+) ([-+*/]) (d+)/);
switch(operator) {
case '+': return +left + +right;
case '-': return +left - right;
case '*': return +left * right;
case '/': return +left / right;
}
}
let calculate = new Calculator()
console.log(calculate.evaluate("2 / 2 + 3 * 4 - 6"));

CertainPerformance已经指出了导致不良行为的错误。

为了正确确定优先级,您应该首先解决所有括号,然后是乘法/除法,然后是加法/减法。

您可以为此使用三个正则表达式(每个一个(,并使用递归来处理括号内的任何表达式。请注意,这样的子表达式不需要只是一个操作。它很可能有更多的操作,甚至括号。因此,括号最好从内到外解决。

以下是调整代码以执行此操作的方法:

const Calculator = function() {
const cal = (a, op, b) => {
switch(op) {
case '+': return +a + +b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
case ')': return +this.evaluate(a); // Use recursion to resolve parentheses
}
};
this.evaluate = string => {
let regexes = [
/(([^()]+)())/g, // Priority 1: parentheses
/(-?d+(?:.d+)?)s*([*/])s*(-?d+(?:.d+)?)/, // Priority 2: multiplication & division
/(-?d+(?:.d+)?)s*([+-])s*(-?d+(?:.d+)?)/, // Priority 3: addition & subtraction
];
for (let regex of regexes) {
let found;
do {
found = false;
string = string.replace(regex, (_, ...args) => {
found = true;
return cal(...args).toFixed(12);
});
} while (found);
}
return string;
};
};
let calculate = new Calculator()
console.log(+calculate.evaluate("2 / 2 + 3 * 4 - 6"));

正则表达式允许小数点(因为除法可能导致小数(和负数(因为减法可能会产生(。所以(d+)变得(-?d+(?:.d+)?)

为了避免将科学记数法插入字符串中,我使用toFixed(12),假设这足以作为精度。

为了提高效率,请查看分流场算法。为了在字符串解析中支持更多的运算符、函数和优先级设置,您可以从这个答案中汲取灵感

最新更新