후위표기법 계산기 기초개념 및 구현실습

2019-04-18

.

그림, 실습코드 등 학습자료 출처 : https://github.com/ythwork

  • 우리가 아는 계산기의 방식은 중위 표기법이다.

ex) (2 + 5) * 3 / (9 - 7)

  • 반면에 컴퓨터가 이해하기 쉬운 계산법은 후위표기법이다.

컴퓨터가 연산하는 과정을 시뮬레이션 한 계산기라고도 할 수 있다.

괄호가 없고, 연산자가 뒤로 가는 것이 특징이다.

ex) 2 5 + 3 * 2 1 + *

이 수식을 푸는 과정

1

  • 후위표기법 계산기 작동예시

(2 + 5) * 3 * (2 + 1) 를 예로들자

먼저 최초 계산에 필요한 요소 셋팅한다.

1) listExp = []

(먼저 최종 후위 수식을 담을 수식리스트)

2) Stack = []

(연산자 가중치에 따라 수식의 요소들을 담을 스택리스트)

그 다음에 각 연산자에 대해 가중치를 책정해둔다.

1) 곱하기, 나누기 : 가중치를 크게 설정

2) 더하기, 빼기 : 가중치를 중간정도 설정

3) 괄호 : 가중치를 가장 작게 설정

예를 들어서 ( 또는 ) 를 말한다.

그 다음에 주어진 수식을 listExp에 담는 작업을 해준다.

2

listExp에 담겨진 후위표기식을 계산하는 작업을 아래의 그림과 같이 해준다.

3

  • 후위표기법 계산기 구현을 위한 stack 클래스 구현
class Node:
    def __init__(self, data=None):
        self.__data=data
        self.__next=None

    @property
    def data(self):
        return self.__data
    
    @data.setter
    def data(self, data):
        self.__data=data
    
    @property
    def next(self):
        return self.__next

    @next.setter
    def next(self, n):
        self.__next=n

class stack:
    def __init__(self):
        self.top=None

    def empty(self):
        if self.top is None:
            return True
        else:
            return False

    def push(self, data):
        new_node = Node(data)
        if self.empty():
            self.top = new_node
            return
        new_node.next = self.top
        self.top=new_node

    def pop(self):
        if self.empty():
            return None
        cur = self.top
        self.top = self.top.next
        return cur.data

    def peek(self):
        if self.empty():
            return None
        return self.top.data

if __name__ =="__main__":
    s = stack()

    s.push(1)
    s.push(2)
    s.push(3)
    s.push(4)
    s.push(5)

    while not s.empty():
        print(s.pop(), end="  ")
5  4  3  2  1  
  • stack 클래스를 이용한 후위표기 계산기 구현
class Calculator:
    def __init__(self):
        self.org_exp=None
        self.postfix_exp=None
        self.result=None
    
    def set_org_exp(self, org_exp):
        self.org_exp=org_exp.replace(' ', '')
        ## org_exp 중위표기법 
        ## ==> 유저에게 입력 받으며 공백을 삭제하여 저장한다

        self.postfix_exp=None
        ## postfix_exp 후위표기법 
        ## ==> 유저에게 입력 받은 중위 표기법을 후위 표기법으로 변경하여 저장한다.

        self.result=None

    def get_org_exp(self):
        return self.org_exp

    def get_weight(self, oprt):
    ## 인자로 받은 연산자의 가중치를 리턴한다.
    ## 가중치 순서 : *, / > +, - > (
        if oprt=='*' or oprt=='/':
            return 9
        elif oprt=='+' or oprt=='-':
            return 7
        elif oprt=='(':
            return 5

    def convert_to_postfix(self):
    ## 인스턴스 변수 org_exp를 후위표기법으로 변경하여 postfix_exp 변수에 저장한다.
    ## 가중치에 따라서 연산자를 담을 스택과, 최종 후위 수식을 담을 리스트를 활용한다.
        exp_list=[]
        oprt_stack=stack()

        for ch in self.get_org_exp(): ## get_orp_exp()에서 입력받은 것들을 for문으로 돌아라
            if ch.isdigit(): ## 숫자라면 exp_list에 넣어주고
                exp_list.append(ch)
            else: ## 연산자 문자열이라면 
                if oprt_stack.empty() or ch=='(': ## 스텍안이 비어있거나 ( 라면
                    oprt_stack.push(ch) ## oprt_stack에 push해라
                elif ch==')': ## ) 라면
                    op=oprt_stack.pop() ## oprt_stack에서 pop해라
                    while op!='(': ## ( 가 나오기 전까지 계속해서
                        exp_list.append(op) ## exp_list에 넣어주고
                        op=oprt_stack.pop() ## oprt_stack에서 pop해라
                else: ## + - / 이라면
                    if self.get_weight(ch) > self.get_weight(oprt_stack.peek()):
                         ## 피크로 확인해서 가중치를 서로 비교해서 새로 가져온게 oprt_stack.peek()보다크면
                         ## oprt_stack으로 새로 가져온 것을 push해라
                        oprt_stack.push(ch)
                    else: ## oprt_stack.peek()보다 가중치가 작거나 같으면
                        while not oprt_stack.empty() and self.get_weight(ch) <= self.get_weight(oprt_stack.peek()):
                            exp_list.append(oprt_stack.pop())
                        oprt_stack.push(ch)
        while not oprt_stack.empty():
            exp_list.append(oprt_stack.pop())

        self.postfix_exp=''.join(exp_list) ## postfix_exp에 문자열로 join해서 넣어라


    def get_postfix_exp(self):
    ## postfix_exp 변수에 담긴 값을 리턴한다. 없다면 convert_to_postfix()을 실행한다.
        if not self.org_exp:
            return None

        if not self.postfix_exp:
            self.convert_to_postfix()

        return self.postfix_exp

    def calc_two_oprd(self, oprd1, oprd2, oprt):
    ## 연산자의 종류에 따라서 연산을 진행한 결과를 리턴한다.
        if oprt=='+':
            return oprd1+oprd2
        elif oprt=='-':
            return oprd1-oprd2
        elif oprt=='*':
            return oprd1*oprd2
        elif oprt=='/':
            return oprd1//oprd2

    def calculate(self):
    ## postfix_exp 변수에 담긴 후위수식 값을 연산한 결과를 리턴한다.
        oprd_stack=stack()
        for ch in self.get_postfix_exp():
            if ch.isdigit():
                oprd_stack.push(int(ch))
            else :
                oprd2=oprd_stack.pop()
                oprd1=oprd_stack.pop()
                oprd_stack.push(self.calc_two_oprd(oprd1, oprd2, ch))
        self.result=oprd_stack.pop()

    def get_result(self):
        if not self.org_exp:
            return None
        
        if not self.result:
            self.calculate()
        
        return self.result

if __name__=="__main__":
    calc=Calculator()

    while True:
        exp=input("수식을 입력하세요 (종료:0):")
        if exp=='0':
            break
        calc.set_org_exp(exp)
        print(calc.get_postfix_exp())
        print('{exp}={result}'.format(exp=calc.get_org_exp(), result=calc.get_result()))
수식을 입력하세요 (종료:0):2+4
24+
2+4=6
수식을 입력하세요 (종료:0):9-7
97-
9-7=2
수식을 입력하세요 (종료:0):5*4
54*
5*4=20
수식을 입력하세요 (종료:0):8/4
84/
8/4=2
수식을 입력하세요 (종료:0):0