1. 计算器

{'id' 学习卡片

任务说明

实现一个支持加、减、乘、除和括号的简单计算器。用户输入一个数学表达式字符串(如 "2 + 3 * (4 - 1)"),程序解析该表达式并输出计算结果。本案例使用递归下降解析法,不依赖 eval(),保证表达式解析过程透明可控。

实现思路

采用递归下降解析器,将表达式文法定义为:

  • expression:处理加减运算(最低优先级)
  • term:处理乘除运算
  • factor:处理数字、括号、正负号

解析器从左到右扫描输入字符串,跳过空格,按优先级逐层解析。除法使用浮点数运算,若除数为零则抛出异常提示。

代码实现

class Calculator:
    """递归下降解析器实现的计算器"""

    def __init__(self, expression: str):
        self.tokens = list(expression.replace(" ", ""))
        self.pos = 0

    def parse(self) -> float:
        """解析完整表达式并返回结果"""
        if not self.tokens:
            raise ValueError("表达式为空")
        result = self._expression()
        if self.pos != len(self.tokens):
            raise ValueError(f"无法解析的字符: {self.tokens[self.pos]}")
        return result

    def _expression(self) -> float:
        """处理加减运算"""
        value = self._term()
        while self.pos < len(self.tokens):
            op = self.tokens[self.pos]
            if op in ('+', '-'):
                self.pos += 1
                right = self._term()
                value = value + right if op == '+' else value - right
            else:
                break
        return value

    def _term(self) -> float:
        """处理乘除运算"""
        value = self._factor()
        while self.pos < len(self.tokens):
            op = self.tokens[self.pos]
            if op in ('*', '/'):
                self.pos += 1
                right = self._factor()
                if op == '*':
                    value *= right
                else:
                    if right == 0:
                        raise ZeroDivisionError("除数不能为零")
                    value /= right
            else:
                break
        return value

    def _factor(self) -> float:
        """处理数字、括号和正负号"""
        if self.pos >= len(self.tokens):
            raise ValueError("表达式不完整")

         = self.tokens[self.pos]

        # 处理一元正负号
        if  in ('+', '-'):
            self.pos += 1
            val = self._factor()
            return val if  == '+' else -val

        # 处理括号
        if  == '(':
            self.pos += 1
            val = self._expression()
            if self.pos >= len(self.tokens) or self.tokens[self.pos] != ')':
                raise ValueError("缺少右括号")
            self.pos += 1
            return val

        # 处理数字(支持整数和小数)
        if .isdigit() or  == '.':
            start = self.pos
            while self.pos < len(self.tokens) and (self.tokens[self.pos].isdigit() or self.tokens[self.pos] == '.'):
                self.pos += 1
            num_str = ''.join(self.tokens[start:self.pos])
            try:
                return float(num_str)
            except ValueError:
                raise ValueError(f"无效数字: {num_str}")

        raise ValueError(f"无法识别的字符: {}")

def main():
    """交互式入口:读取用户输入并计算结果"""
    while True:
        try:
            expr = input("请输入表达式(输入 q 退出): ").strip()
            if expr.lower() == 'q':
                break
            if not expr:
                continue
            calc = Calculator(expr)
            result = calc.parse()
            # 输出整数时不带小数点
            if result == int(result):
                print(f"结果: {int(result)}")
            else:
                print(f"结果: {result}")
        except (ValueError, ZeroDivisionError) as e:
            print(f"错误: {e}")

if __name__ == "__main__":
    main()

运行与验证

运行方式:将代码保存为 calculator.py,在终端执行 python calculator.py,然后输入表达式进行测试。

验证用例与预期输出:

输入: 2 + 3 * 4
输出: 14

输入: (2 + 3) * 4
输出: 20

输入: 10 / 4
输出: 2.5

输入: -3 + 5
输出: 2

输入: 2 * (3 + 4) - 6 / 2
输出: 11

输入: 5 / 0
输出: 错误: 除数不能为零

输入: (1 + 2
输出: 错误: 缺少右括号

判定标准:上述用例的输出与预期一致,且程序能正确处理括号嵌套、运算符优先级、一元正负号及除零错误,即判定实现通过。

解析过程示意

表达式 "2 + 3 * (4 - 1)" 递归下降解析过程 expression: 2 + term(3 * factor(4-1)) term: 3 * factor(4-1) factor: ( expression: 4 - 1 ) 结果 = 2 + 3 * 3 = 11 优先级: 低 优先级: 高

解析器从最外层的 expression 开始,遇到加减号则继续处理右侧的 term;term 内部处理乘除;遇到括号时递归调用 _expression() 解析括号内子表达式。这种层级结构天然保证了运算优先级:先括号、再乘除、最后加减。