JavaScript 如何定位字符串中开闭括号位置 实现带阶乘的括号运算计算器
现有逻辑的核心问题
- 目前的切分逻辑只会逐字符扫描普通运算符,完全忽略括号的层级边界:只要遇到
+/-/*//就记录为切分点,哪怕这个运算符藏在括号内部,最终会把(2+3)!这类带括号的表达式从中间劈成(2和3)!两段,后续给带!的片段套阶乘的时候,就会生成错误的(2+factorial(3))结构,完全不符合数学运算规则。 - 阶乘识别逻辑没有边界校验:只要切出来的片段里带
!,就直接把整段包成factorial(),完全不判断!前面对应的操作数是单个数字,还是一整个配对完整的括号表达式,最终出现运算顺序错误。 - 手动切分运算段的逻辑本身是冗余的:最终是用
Function方法直接对字符串求值,JS本身就支持正确的运算符优先级、括号嵌套解析,根本不需要手动把表达式拆成操作数和运算符数组,这套手写切分逻辑反而是所有异常的来源。
可直接替换的实现方案
不需要保留原来的切分逻辑,直接把预处理的核心改成优先识别阶乘操作的完整边界,不拆分裂表达式结构,替换后的函数如下:
function sepOperFat(){ const rawValue = document.calculator.ans.value; let processedStr = rawValue; // 逐次扫描处理所有阶乘符号 let exclaimIndex = processedStr.indexOf('!'); while (exclaimIndex !== -1) { let operandStart = exclaimIndex - 1; let isBracketOperand = false; // 定位!对应的完整操作数起点 if (processedStr[operandStart] === ')') { isBracketOperand = true; // 如果!前面是右括号,反向匹配对应左括号,找到完整的括号块边界 let bracketDepth = 1; while (bracketDepth !== 0 && operandStart > 0) { operandStart--; if (processedStr[operandStart] === ')') bracketDepth++; if (processedStr[operandStart] === '(') bracketDepth--; } } else { // 如果!前面是数字,把连续的数字、小数点都纳入操作数范围 while (operandStart > 0 && /[0-9.]/.test(processedStr[operandStart])) { operandStart--; } operandStart++; } // 提取完整操作数,括号块去掉外层冗余括号 const fullOperand = isBracketOperand ? processedStr.slice(operandStart + 1, exclaimIndex - 1) : processedStr.slice(operandStart, exclaimIndex); // 把定位到的完整操作数替换为factorial调用 processedStr = processedStr.slice(0, operandStart) + `factorial(${fullOperand})` + processedStr.slice(exclaimIndex + 1); // 继续找下一个!的位置 exclaimIndex = processedStr.indexOf('!'); } // 替换计算器自定义的乘除符号为JS可识别的语法 processedStr = processedStr.replaceAll('×', '*').replaceAll('÷', '/'); // 写回处理后的表达式 document.calculator.ans.value = processedStr; }
方案验证效果
- 输入
(2+3)!:会被正确解析为factorial(2+3),求值结果120,不会再把括号内的加号当成切分点 - 输入
2+(3)!:会被正确解析为2+factorial(3),不会生成错误的嵌套结构 - 输入目标场景
1+(2-(2+2)+3)!:会被解析为1+factorial(2-(2+2)+3),无论括号嵌套多少层,都能准确识别阶乘的作用范围 - 原有基础场景兼容:
2+3*4、20+5!这类简单表达式的求值结果和之前完全一致,不会出现回归问题
内容的提问来源于stack exchange,提问作者Otávio Zampar
相关产品推荐
相关产品推荐

