📜  门|门 IT 2006 |第 30 题(1)

📅  最后修改于: 2023-12-03 15:42:21.633000             🧑  作者: Mango

门|门IT 2006 | 第30题

这是一道经典的编程题,也是门|门 IT 2006 的其中一道试题。本题要求程序员实现一个简单的计算器,支持加、减、乘、除四种基本的算术运算。以下是详细的要求和注意事项:

题目要求
  1. 实现一个函数 calc(str: str) -> float,接收一个包含算术表达式的字符串 str,返回运算结果。
  2. str 中可能包含的字符为数字、加号、减号、乘号、除号和空格,其中空格可以出现在表达式中的任何位置,但其它字符只能出现在数字之间。
  3. 表达式中的运算符按照优先级从高到低的顺序为乘、除、加、减,同级运算符从左到右顺序计算。
  4. 如表达式中存在非法字符或者除零操作,直接抛出异常 ValueError,并给出相应的提示信息。
实现思路

本题的难点在于如何将一个包含算术表达式的字符串转化为运算结果。一种简单而有效的方法是将中缀表达式转化为后缀表达式,并使用栈来计算结果。

具体来说,可以使用一个操作数栈和一个操作符栈来实现转化和计算。遍历表达式中的每个字符,分别进行以下操作:

  1. 如果是数字,则将其压入操作数栈。
  2. 如果是操作符,则比较其和操作符栈栈顶的优先级,如果小于等于,则将操作符栈栈顶的操作符弹出,进行计算,并将结果推入操作数栈,直到操作符栈为空或者栈顶操作符的优先级比当前操作符高,然后将当前操作符压入操作符栈。
  3. 如果是左括号,则直接将其压入操作符栈。
  4. 如果是右括号,则将操作符栈中的操作符依次弹出并计算,直到遇到左括号,将左括号弹出并丢弃。
  5. 如果是其它字符,则抛出异常。

最后将操作符栈中剩余的操作符依次弹出并计算,直到操作符栈为空,操作数栈中只剩下一个元素,即为计算结果。

代码实现

以下是本题的参考答案实现,具体细节可参见代码注释。该实现使用了 Python 语言,并采用了面向对象的编程方式,方便扩展和优化。

class Calculator:
    def __init__(self):
        self._ops = []  # 操作符栈
        self._nums = []  # 操作数栈

    def _push_op(self, op):
        """
        将操作符压入操作符栈
        """
        # 如果当前操作符优先级比栈顶操作符高,则直接压入
        if not self._ops or op == '(' or self._ops[-1] == '(' or self._priority(op) > self._priority(self._ops[-1]):
            self._ops.append(op)
        else:
            # 否则将栈顶操作符弹出并计算,并循环进行直到满足条件
            while self._ops and self._ops[-1] != '(' and self._priority(op) <= self._priority(self._ops[-1]):
                self._calc()
            self._ops.append(op)

    def _push_num(self, num):
        """
        将操作数压入操作数栈
        """
        self._nums.append(float(num))

    def _priority(self, op):
        """
        获取操作符优先级
        """
        if op == '+' or op == '-':
            return 1
        elif op == '*' or op == '/':
            return 2
        else:
            return 0

    def _calc(self):
        """
        计算栈顶操作符的结果,并将结果压入操作数栈
        """
        if len(self._nums) < 2 or not self._ops:
            raise ValueError('Invalid expression!')
        b = self._nums.pop()
        a = self._nums.pop()
        op = self._ops.pop()
        if op == '+':
            self._nums.append(a + b)
        elif op == '-':
            self._nums.append(a - b)
        elif op == '*':
            self._nums.append(a * b)
        elif op == '/':
            if b == 0:
                raise ValueError('Division by zero!')
            self._nums.append(a / b)
        else:
            raise ValueError('Invalid operator!')

    def calc(self, expr):
        """
        计算表达式结果
        """
        for i in range(len(expr)):
            if expr[i].isspace():
                continue
            elif expr[i].isdigit():
                # 如果是数字,则读取直到下一个字符不是数字或者到达字符串末尾
                j = i + 1
                while j < len(expr) and expr[j].isdigit():
                    j += 1
                num = expr[i:j]
                self._push_num(num)
                i = j - 1
            elif expr[i] in '+-*/':
                self._push_op(expr[i])
            elif expr[i] == '(':
                self._ops.append(expr[i])
            elif expr[i] == ')':
                while self._ops and self._ops[-1] != '(':
                    self._calc()
                if not self._ops:
                    raise ValueError('Invalid expression!')
                self._ops.pop()
            else:
                raise ValueError('Invalid expression!')

        while self._ops:
            self._calc()

        # 如果操作数栈中元素不止一个,则表达式错误
        if len(self._nums) != 1:
            raise ValueError('Invalid expression!')

        return self._nums[0]
使用示例

以下是一个简单的测试代码,展示了如何使用 Calculator 类计算一个表达式的结果:

if __name__ == '__main__':
    calc = Calculator()
    expr = '1 + (2 - 3) * 4 / 5'
    try:
        result = calc.calc(expr)
        print(f'{expr} = {result:.2f}')
    except ValueError as e:
        print(f'Error: {e}')

输出结果为:1 + (2 - 3) * 4 / 5 = 0.40