Python/Python

[Python] 후위표기법(postifx) 계산 코드

상쾌한기분 2022. 5. 12. 16:15
728x90
반응형

후위표기법(postfix) 계산 코드

from typing import List, Optional


class Profit(object):
    __slots__ = ['subject_name', 'profit_value']

    def __init__(self, subject_name: str, profit_value: float):
        self.subject_name = subject_name
        self.profit_value = profit_value


def find_profit_by_subject_name(profits: List[Profit], subject_name: str) -> Optional[Profit]:
    for p in profits:
        if p.subject_name == subject_name:
            return p
    return None


class PostfixBase:
    __slots__ = ['subject_formula', ]

    operations = ["*", "/", "+", "-"]
    operations_priority = { "*": 3, "/": 3, "+": 2, "-": 2, "(": 1 }
    calculations = {
        "*": lambda x, y: x * y,
        "/": lambda x, y: x / y,
        "+": lambda x, y: x + y,
        "-": lambda x, y: x - y,
    }

    def __init__(self, subject_formula: str):
        self.subject_formula = subject_formula


class PostfixConverter(PostfixBase):

    def convert_formula_to_list(self) -> list:
        res = []
        before, after = 0, 0

        for i, f in enumerate(self.subject_formula):
            if f in self.operations:
                res.append(self.subject_formula[before:after + 1])
                res.append(f)
                before = i + 1
            else:
                after = i
                if i == len(self.subject_formula) - 1:
                    res.append(self.subject_formula[before:after + 1])

        print(res)
        return res

    def convert_formula_list_to_postfix(self):
        formula_list = self.convert_formula_to_list()
        stack, res = [], []

        for f in formula_list:
            if f not in self.operations:
                res.append(f)
            elif f == "(":
                stack.append(f)
            elif f == ")":
                while stack != [] and stack[-1] != "(":
                    res.append(stack.pop())
                stack.pop()
            else:
                while stack != [] and self.operations_priority[stack[-1]] >= self.operations_priority[f]:
                    res.append(stack.pop())
                stack.append(f)
        while stack:
            res.append(stack.pop())

        print(res)
        return res


class PostfixCalculator(PostfixBase):

    def __init__(self, subject_formula: str):
        super().__init__(subject_formula)
        self.postfix_converter = PostfixConverter(subject_formula)

    def calculate(self):
        postfix_formula = self.postfix_converter.convert_formula_list_to_postfix()

        stack = []

        for p in postfix_formula:
            if p not in self.operations:
                profit = find_profit_by_subject_name(profits, p)
                if profit:
                    stack.append(profit.profit_value)
                else:
                    if p.isdigit():
                        stack.append(float(p))
                    else:
                        # subject not found case
                        stack.append(1.0)
            else:
                value1, value2 = stack.pop(), stack.pop()
                # print(value1, p, value2)
                if p == "/" and (value1 == 0 or value2 == 0):
                    raise Exception("can't divide zero")

                stack.append(self.calculations[p](value2, value1))

        return stack.pop()


################################################################################################################################

profits = [
    Profit("Product costs", 10.0),
    Profit("Sales revenue", 1.0),

    Profit("Marketing", 1.1),
    Profit("PG commission", 2.0),

]

subject_formulas = [
    "1-Product costs/Sales revenue",
    "Marketing+PG commission+Labor costs(D)+Internal fee+Service fee_external+Packing material"
]

for formula in subject_formulas:
    calculator = PostfixCalculator(formula)
    value = calculator.calculate()
    print(value)

 

['1', '-', 'Product costs', '/', 'Sales revenue']
['1', 'Product costs', 'Sales revenue', '/', '-']
-9.0

['Marketing', '+', 'PG commission', '+', 'Labor costs(D)', '+', 'Internal fee', '+', 'Service fee_external', '+', 'Packing material']
['Marketing', 'PG commission', '+', 'Labor costs(D)', '+', 'Internal fee', '+', 'Service fee_external', '+', 'Packing material', '+']
7.1
728x90
반응형