You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

后缀表达式计算器变量输入错误排查及修复请求

基于栈的后缀表达式计算器变量处理问题修复

问题描述

我实现了一个基于栈的后缀表达式计算器,普通表达式如2 5 +可正常运行,但变量处理时出现错误,运行输出如下:

7
27

报错信息:

index.js:44
                throw new Error(`Invalid token encountered: ${token}`);
                ^

    Error: Invalid token encountered: A
        at PostfixCalculator.evaluate (index.js:44:19)
        at Object.<anonymous> (/runtime/javascript/3zxrd96tq_3zxykq39r/index.js:78:24)
        at Module._compile (node:internal/modules/cjs/loader:1103:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
        at Module.load (node:internal/modules/cjs/loader:981:32)
        at Function.Module._load (node:internal/modules/cjs/loader:822:12)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
        at node:internal/main/run_main_module:17:47

附原代码:

class PostfixCalculator {
  constructor() {
    // Initialize an empty stack to hold operands and an empty symbol table for variables
    this.stack = [];
    this.symbolTable = {};
  }

  // Method to evaluate postfix expressions
  evaluate(expression) {
    // Split the expression into tokens (operands, operators, or variables) using spaces
    const tokens = expression.split(' ');

    // Iterate through each token in the expression
    for (let token of tokens) {
      if (!isNaN(token)) {
        // If the token is a number, push it onto the stack as an operand
        this.stack.push(parseFloat(token)); // Push operands to the stack
      } else if (token in this.symbolTable) {
        // If the token is a known variable, push its corresponding value onto the stack
        this.stack.push(this.symbolTable[token]); // Push variable values to the stack
      } else {
        // If the token is an operator (+, -, *, /) or '=', perform the corresponding operation
        switch (token) {
          case '+':
            this.performOperation((a, b) => a + b); // Addition operation
            break;
          case '-':
            this.performOperation((a, b) => a - b); // Subtraction operation
            break;
          case '*':
            this.performOperation((a, b) => a * b); // Multiplication operation
            break;
          case '/':
            this.performOperation((a, b) => a / b); // Division operation
            break;
          case '=':
            // Perform variable assignment by popping value and variable name from the stack
            const value = this.stack.pop();
            const variable = this.stack.pop();
            this.symbolTable[variable] = value; // Store variable value in the symbol table
            break;
          default:
            // Throw an error for an invalid token encountered
            throw new Error(`Invalid token encountered: ${token}`);
        }
      }
    }

    // Check if the stack contains only one element after evaluation
    if (this.stack.length !== 1) {
      throw new Error('Invalid expression');
    }

    // Return the final result by popping the last element from the stack
    return this.stack.pop(); // Result will be the only element in the stack
  }

  // Method to perform arithmetic operations on operands
  performOperation(operation) {
    // Pop the last two operands from the stack
    const operand2 = this.stack.pop();
    const operand1 = this.stack.pop();
    // Perform the specified operation on the operands
    const result = operation(operand1, operand2);
    // Push the result back onto the stack
    this.stack.push(result);
  }
}

// Example usage:
const calculator = new PostfixCalculator();

// Evaluate postfix expressions
console.log(calculator.evaluate('3 4 +')); // Output: 7
console.log(calculator.evaluate('3 4 5 + *')); // Output: 27

// Handle variable assignments
console.log(calculator.evaluate('A 2 =')); // Setting variable A to 2
console.log(calculator.evaluate('B 3 =')); // Setting variable B to 3
console.log(calculator.evaluate('A B *')); // Output: 6 (Multiplication of variables A and B)

错误原因分析

  1. 赋值逻辑顺序错误:
    • 后缀表达式的赋值规则是值 变量名 =,但你写的A 2 =顺序颠倒,且代码中赋值时的栈弹出顺序也反了(先弹值再弹变量名,正确应该是先弹变量名再弹值)。
  2. 变量处理逻辑漏洞:
    • 代码仅允许压入已存在的变量值,但赋值时变量名是目标标识符,此时还未定义,需要直接将变量名字符串压入栈,而不是判断是否存在于symbolTable中。

修复后的代码

class PostfixCalculator {
  constructor() {
    this.stack = [];
    this.symbolTable = {};
  }

  // 解析栈中的元素,若是变量名则返回对应值,否则返回自身
  resolveValue(item) {
    if (typeof item === 'string') {
      if (!(item in this.symbolTable)) {
        throw new Error(`Undefined variable: ${item}`);
      }
      return this.symbolTable[item];
    }
    return item;
  }

  evaluate(expression) {
    // 每次计算前清空栈,避免残留数据影响结果
    this.stack = [];
    const tokens = expression.split(' ');

    for (let token of tokens) {
      // 先判断是否是运算符
      if (['+', '-', '*', '/', '='].includes(token)) {
        switch (token) {
          case '+':
            this.performOperation((a, b) => a + b);
            break;
          case '-':
            this.performOperation((a, b) => a - b);
            break;
          case '*':
            this.performOperation((a, b) => a * b);
            break;
          case '/':
            this.performOperation((a, b) => {
              if (b === 0) throw new Error('Division by zero');
              return a / b;
            });
            break;
          case '=':
            // 赋值:先弹出变量名,再弹出值
            const variable = this.stack.pop();
            const value = this.resolveValue(this.stack.pop());
            // 确保变量名是字符串类型
            if (typeof variable !== 'string') {
              throw new Error('Invalid variable name');
            }
            this.symbolTable[variable] = value;
            // 赋值操作不返回结果,将变量值压回栈以便后续使用
            this.stack.push(value);
            break;
          default:
            throw new Error(`Invalid token encountered: ${token}`);
        }
      } else if (!isNaN(token)) {
        // 是数字则转为浮点数压栈
        this.stack.push(parseFloat(token));
      } else {
        // 是变量名直接压栈(字符串)
        this.stack.push(token);
      }
    }

    if (this.stack.length !== 1) {
      throw new Error('Invalid expression');
    }

    // 解析最终结果(可能是变量名)
    return this.resolveValue(this.stack.pop());
  }

  performOperation(operation) {
    const operand2 = this.resolveValue(this.stack.pop());
    const operand1 = this.resolveValue(this.stack.pop());
    const result = operation(operand1, operand2);
    this.stack.push(result);
  }
}

// 修正后的示例用法
const calculator = new PostfixCalculator();

console.log(calculator.evaluate('3 4 +')); // 7
console.log(calculator.evaluate('3 4 5 + *')); // 27

// 正确的赋值表达式:值在前,变量名在后
console.log(calculator.evaluate('2 A =')); // 2(返回赋值后的值)
console.log(calculator.evaluate('3 B =')); // 3
console.log(calculator.evaluate('A B *')); // 6

优化建议

  • 变量名合法性检查:添加正则判断变量名是否符合标识符规则(比如不能以数字开头)。
  • 错误信息细化:区分无效token、未定义变量、非法变量名等不同错误场景。
  • 表达式验证:处理前检查表达式的token数量是否符合后缀表达式规则。
  • 支持负数:修改token分割逻辑或判断规则,支持负数字面量(如-5)。

内容的提问来源于stack exchange,提问作者itxfahad_

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:08:10