棧實現簡單四則運算


棧是一種操作受限的線性表,只允許在一端插入或刪除數據,后進先出,先進后出,就是典型的棧結構。

棧主要包含2個操作,入棧和出棧,也就是在棧頂插入或刪除一個元素。

實現一個基於數組的順序棧:

class Stack:
    def __init__(self, cap=64):
        self.L = []
        self.cap = cap
        self.cnt = 0

    def push(self, data):
        if self.cnt == self.cap:
            return False
        self.L.append(data) 
        self.cnt += 1
        return True

    def pop(self):
        if self.cnt == 0:
            return None
        v = self.L.pop()
        self.cnt -= 1
        return v

棧在表達式求值中的應用

將表達式簡化為只包含加、減、乘、除四則運算,例如:3 + 2 * 4 - 1,人腦可以迅速得出答案,但是對於計算機來說,理解這個表達式就是一件很難的事。

我們可以使用2個棧,一個保存數據,另一個保存操作符,從左向右遍歷表達式,當遇到數字就直接壓入數據棧,遇到操作符時就與操作符棧頂元素比較:

  • 如果比棧頂操作符優先級高,直接將當前運算符入棧
  • 如果比棧頂元素優先級低或者相同,則從操作符棧取出元素,再從數據棧取出2個元素進行計算,結果保存到數據棧,然后繼續比較

用代碼實現如下:

class UnsupportExpressionError(Exception):
    pass


class UnsupportOperationError(Exception):
    pass


class SimpleCalculator:
    _priority = {
        '+': 10, '-': 10,
        '*': 20, '/': 20,
    }
    _digits = {
        '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, 
        '5': 5, '6': 6, '7': 7, '8': 8, '9': 9
    }

    def __init__(self, expression):
        self.symbol_stack = Stack()
        self.number_stack = Stack()
        self.expression = expression
        self._result = self.get_result()

    @property
    def result(self):
        return self._result

    def get_result(self):
        decimal = 0
        for x in self.expression:
            v = x.strip()
            if not v:
                continue
            
            if v in self._priority:
                self.number_stack.push(decimal)
                decimal = 0
                self.cmp_and_calc(v)
                self.symbol_stack.push(v)
            elif v in self._digits:
                decimal = decimal * 10 + self._digits[v]
            else:
                raise UnsupportExpressionError(f'unsupport operation: "{v}" in expression: {self.expression}')

        self.number_stack.push(decimal)
        operation = self.symbol_stack.pop()
        while operation:
            ret = self._calc_helper(operation)
            self.number_stack.push(ret)
            operation = self.symbol_stack.pop()
        ret = self.number_stack.pop()

        return ret

    def cmp_and_calc(self, operation):
        symbol = self.symbol_stack.pop()
        if symbol is None:
            return
        if self._priority[operation] > self._priority[symbol]:
            self.symbol_stack.push(symbol)
        else:
            ret = self._calc_helper(symbol)
            self.number_stack.push(ret)
            self.cmp_and_calc(operation)

    def _calc_helper(self, operation):
        b = self.number_stack.pop()
        a = self.number_stack.pop()
        if a is None:
            return b
        
        if operation == '+':
            return a + b
        elif operation == '-':
            return a - b
        elif operation == '*':
            return a * b
        elif operation == '/':
            return a / b
        else:
            raise UnsupportOperationError(f'unsupport operation: {operation}')

def main():
    s = ''
    while not s or not s.strip():
        s = input('please enter an expression: ')
    c = SimpleCalculator(s)
    print(c.result)

帶括號的表達式求值

同理可以使用一個棧保存表達式,當遇到右括號時,將該括號對里面的表達式使用上面的 SimpleCalculator 計算,得出的結果再壓入棧,最后得到的表達式就是一個不含括號的表達式了。

例如表達式:2*(6/(1+2)) 計算過程:

  1. 將 '2*(6/(1+2' 依次入棧
  2. 遇到 ')' ,不斷從棧中取出數據,直到有匹配的左括號
  3. 然后計算 '1+2' 將結果 3 入棧
  4. 遇到 ')' ,計算 '6/3' 將結果 2.0 入棧
  5. 最再計算表達式 2*2.0 得出最終結果: 4.0

SimpleCalculator 不支持小數,改寫 get_result 函數:

    def get_result(self):
        decimal = 0
        has_dot = False
        factor = 0
        for x in self.expression:
            v = x.strip()
            if not v:
                continue
            
            if v == '.':
                has_dot = True
                factor = 0.1
            elif v in self._priority:
                self.number_stack.push(decimal)
                decimal = 0
                has_dot = False
                factor = 0
                self.cmp_and_calc(v)
                self.symbol_stack.push(v)
            elif v in self._digits:
                if not has_dot:
                    decimal = decimal * 10 + self._digits[v]
                else:
                    decimal = decimal + self._digits[v] * factor
                    factor = factor * 0.1
            else:
                raise UnsupportExpressionError(f'unsupport operation: "{v}" in expression: {self.expression}')

        self.number_stack.push(decimal)
        operation = self.symbol_stack.pop()
        while operation:
            ret = self._calc_helper(operation)
            self.number_stack.push(ret)
            operation = self.symbol_stack.pop()
        ret = self.number_stack.pop()

        return ret

帶括號的表達式求解:

class BracketsCalculator:
    _match_pair = {
        ')': '(',
        ']': '[',
        '}': '{',
    }
    def __init__(self, expression):
        self.exp_stack = Stack()
        self.expression = expression
        self._result = self.get_result()

    @property
    def result(self):
        return self._result

    def get_result(self):
        exp = ''
        for x in self.expression:
            v = x.strip()
            if not v:
                continue

            if v in (')', ']', '}'):
                val = self.exp_stack.pop()
                while val != self._match_pair[v]:
                    exp = f'{val}{exp}'
                    val = self.exp_stack.pop()
                
                ret = SimpleCalculator(exp).result
                self.exp_stack.push(ret)
                exp = ''
            else:
                self.exp_stack.push(v)
        
        val = self.exp_stack.pop()
        while val is not None:
            exp = f'{val}{exp}'
            val = self.exp_stack.pop()
        
        return SimpleCalculator(exp).result


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM