강의노트 21. 자료구조 - stack을 이용한 후위표기법 계산기 만들기
18 Apr 2017 |패스트캠퍼스 컴퓨터공학 입문 수업을 듣고 중요한 내용을 정리했습니다. 개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
후위표기법(postfix notation)
- 수업자료
- 연산자를 피연산자의 뒤에 놓는 방법 (사람이 사용하는 수식의 표기 방법 - 중위(infix) 표기법)
- 괄호가 없어도 계산 순서가 일정하다.
- 컴퓨터에서의 수식의 계산은 (1) 수식을 후위표기식으로 바꾸고 (2) 후위표기식을 계산하는 두 과정으로 이루어진다.
후위표기법 만들기
- 상세 내용은 상기 수업 자료를 참고한다.
준비물
- listExp : 최종 후위 수식을 담을 리스트
- stack : LIFO 방식의 자료구조, 연산자를 가중치에 따라서 담을 스택 리스트
연산자 우선순위
*, /
: 제일 크다+, -
: 두번째(
:가장 작다
규칙
- 중위표기법 : (2 + 5) * 3 * (2 + 1) ==> 후위표기법: 25 + 3 * 21 + *
- 숫자가 오면 listExp 에 담는다.
- 숫자가 아닌 연산자는 stack에 쌓는다.
- stack 의 맨 위 연산자의 가중치가 새로 추가되는 연산자의 가중치 보다 높거나 같다면 listExp로 옮긴다.
(
는 가중치에 상관 없이무조건 stack에 쌓는다
.)
를 만나는 순간(
위에 있는 모든 연산자를 listExp에 pop 으로 넣고(
는 없앤다.
stack을 이용한 후위표기법 계산기 만들기
ADT 인터페이스
Data
org_exp # 중위표기법 (유저에게 입력 받으며 공백을 삭제하여 저장한다)
postfix_exp # 후위표기법 (유저에게 입력 받은 중위 표기법을 후위 표기법으로 변경하여 저장한다.)
Opertion
.get_weight(self, oprt)
# 인자로 받은 연산자의 가중치를 리턴한다.
# 가중치 순서 : *, / > +, - > (
.convert_to_postfix(self)
# 인스턴스 변수 org_exp를 후위표기법으로 변경하여 postfix_exp 변수에 저장한다.
# 가중치에 따라서 연산자를 담을 스택과, 최종 후위 수식을 담을 리스트를 활용한다.
.get_postfix_exp(self)
# postfix_exp 변수에 담긴 값을 리턴한다. 없다면 convert_to_postfix()을 실행한다.
.calc_two_oprd(self, 피연산자1, 피연산자2, 연산자)
# 연산자의 종류에 따라서 연산을 진행한 결과를 리턴한다.
.calculate(self)
# postfix_exp 변수에 담긴 후위수식 값을 연산한 결과를 리턴한다.
구현코드
from stack import Stack
class Calculator:
def __init__(self, org_exp):
self.org_exp = org_exp.replace(' ', '')
self.postfix_exp = None
def get_weight(self, oprt):
if oprt == '*' or oprt == '/':
return 9
elif oprt == '+' or oprt == '-':
return 7
elif oprt == '(':
return 5
else:
return -1
def convert_to_postfix(self):
exp_list = []
oprt_stack = Stack()
for ch in self.org_exp:
if ch.isdigit():
exp_list.append(ch)
#연산자라면
else:
#'(' or 연산자 스택이 비었다면
if ch == '(' or oprt_stack.is_empty():
oprt_stack.push(ch)
#')'
elif ch == ')':
op = oprt_stack.pop()
while op != '(':
exp_list.append(op)
op = oprt_stack.pop()
#'+', '-', '*', '/' : 가중치가 높을 때
elif self.get_weight(ch) > \
self.get_weight(oprt_stack.peek()):
oprt_stack.push(ch)
#'+', '-', '*', '/' : 가중치가 낮거나 같을 때
else:
while oprt_stack and self.get_weight(ch) <=\
self.get_weight(oprt_stack.peek()):
exp_list.append(oprt_stack.pop())
oprt_stack.push(ch)
while oprt_stack:
exp_list.append(oprt_stack.pop())
self.postfix_exp = ''.join(exp_list)
def get_postfix_exp(self):
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):
oprd_stack = Stack()
for ch in self.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))
return oprd_stack.pop()
if __name__ == "__main__":
a = input("수식을 입력하세요 : ")
calc = Calculator(a)
print(calc.get_postfix_exp())
print("{exp} = {result}".format(
exp = calc.org_exp, result = calc.calculate()))