공백으로 구분되지 않는 수식을 입력 받았을때 계산을 위한 토큰화를 하자
수식은 크게 수식 연산자 수식 으로 되어 있다
일단 괄호는 없다고 생각하자.
숫자 연산주 숫자로 나누기 위해
우선 숫자와 연산자를 찾아야 합니다.
숫자를 읽다가 연산자가 나타나면 숫자와 연산자를 차례대로 tokens에 넣다
def tokenizer(expression):
expression = list(expression.replace(" ", "")) # 공백 제거
tokens = []
operand = ""
operator = ""
for i, v in enumerate(expression):
if v in '01234567890':
operand += v
elif v in '+-*/':
tokens.append(operand)
operand = ""
tokens.append(v)
else:
raise ValueError(f"Invalid character '{v}' in expression.")
return tokens + [operand] if operand else tokens
하지만 이렇게 하면 숫자가 -일 경우 처리가 안된다.
숫자에 -가 붙는 경우는 숫자의 첫번째 뿐이니 operand == ''(NULL)일 경우만 처리해주면 된다. 숫자로서 첫번째 글자가 숫자가 아니라면 에러 처리를 해준다. 이렇게 하면 자동적으로 연산자가 2개이상 오는것도 에러처리된다.
def tokenizer(expression):
expression = list(expression.replace(" ", "")) # 공백 제거
tokens = []
operand = ""
operator = ""
for i, v in enumerate(expression):
if operand == '':
if v in '01234567890-+':
operand = v
else:
raise ValueError(f"Invalid character '{v}' in expression.")
elif v in '01234567890.':
operand += v
elif v in '+-*/':
tokens.append(operand)
operand = ""
tokens.append(v)
else:
raise ValueError(f"Invalid character '{v}' in expression.")
return tokens + [operand] if operand else tokens
테스트 해보니 생각보다 잘된다
# 덧셈 함수
def add(a, b):
return a + b
# 뺄셈 함수
def subtract(a, b):
return a - b
# 곱셈 함수
def multiply(a, b):
return a * b
# 나눗셈 함수 (0으로 나눌 경우 예외 발생)
def divide(a, b):
if b == 0:
raise ZeroDivisionError
return a / b
def tokenizer(expression):
expression = list(expression.replace(" ", "")) # 공백 제거
tokens = []
operand = ""
operator = ""
for i, v in enumerate(expression):
if operand == '':
if v in '01234567890-+':
operand = v
else:
raise ValueError(f"Invalid character '{v}' in expression.")
elif v in '01234567890.':
operand += v
elif v in '+-*/':
tokens.append(operand)
operand = ""
tokens.append(v)
else:
raise ValueError(f"Invalid character '{v}' in expression.")
return tokens + [operand] if operand else tokens
# 연산 우선순위를 고려한 계산 함수
def calculate(tokens):
# 1단계: 곱셈(*)과 나눗셈(/) 먼저 처리
i = 0
while i < len(tokens):
if tokens[i] in ("*", "/"):
try:
left = float(tokens[i - 1]) # 왼쪽 피연산자
right = float(tokens[i + 1]) # 오른쪽 피연산자
if tokens[i] == "*":
result = multiply(left, right)
else:
result = divide(left, right)
# 계산된 결과를 기준으로 토큰 리스트를 갱신
tokens = tokens[:i - 1] + [str(result)] + tokens[i + 2:]
i -= 1 # 리스트가 줄었으므로 인덱스 조정
except (ValueError, ZeroDivisionError):
raise
else:
i += 1
# 2단계: 덧셈(+)과 뺄셈(-) 처리
i = 0
while i < len(tokens):
if tokens[i] in ("+", "-"):
try:
left = float(tokens[i - 1])
right = float(tokens[i + 1])
if tokens[i] == "+":
result = add(left, right)
else:
result = subtract(left, right)
# 계산된 결과를 기준으로 토큰 리스트를 갱신
tokens = tokens[:i - 1] + [str(result)] + tokens[i + 2:]
i -= 1
except ValueError:
raise
else:
i += 1
# 최종 결과 반환
return float(tokens[0])
# 메인 함수
def main():
try:
# 사용자로부터 수식 입력 받기
expression = input("Enter expression: ")
tokens = tokenizer(expression) # 공백으로 토큰화
# 토큰 수가 올바르지 않으면 오류 발생
if len(tokens) < 3 or len(tokens) % 2 == 0:
raise ValueError("Invalid token count.")
# 피연산자(짝수 인덱스)와 연산자(홀수 인덱스) 검증
for i, token in enumerate(tokens):
if i % 2 == 0:
float(token) # 피연산자가 숫자인지 확인
else:
if token not in ("+", "-", "*", "/"):
raise ValueError("Invalid operator.")
# 계산 수행
result = calculate(tokens)
print(f"Result: {result}")
# 0으로 나누는 경우 예외 처리
except ZeroDivisionError:
print("Error: Division by zero.")
# 그 외의 모든 잘못된 입력에 대한 처리
except Exception:
print("Invalid input.")
# 프로그램의 시작점
if __name__ == "__main__":
main()
GPT가 위 코드를보고 평가해주고 고쳐주네
✅ 현재 tokenizer()의 장점
- 공백 없이 연산자와 숫자를 정확하게 분리
- 음수를 숫자의 일부로 간주 (예: -3, +2 등)
⚠️ 개선이 필요한 부분
- -나 +가 단항 연산자로 쓰일 때와 이항 연산자로 쓰일 때를 구분하지 않고 처리
- "3+-2"는 ['3', '+', '-2']로 잘 나뉘지만, "3 - -2" 같은 경우는 잘못 처리될 가능성이 있음
- 괄호가 포함된 표현은 처리하지 않음
🎯 개선 방향
- - 또는 +가 앞에 연산자나 괄호가 있을 때는 음수 부호로 해석
- 그 외에는 이항 연산자로 간주
- 괄호는 없다고 가정하거나 추가 처리
🧩 우선 목표: tokenizer를 기존 calculate() 함수와 통합해서 음수를 처리하는 완성형 프로그램 만들기
✅ 통합 코드 (음수 처리 포함)
def add(a, b): return a + b
def subtract(a, b): return a - b
def multiply(a, b): return a * b
def divide(a, b):
if b == 0: raise ZeroDivisionError
return a / b
# 개선된 tokenizer
def tokenizer(expression):
expression = expression.replace(" ", "") # 공백 제거
tokens = []
num = ""
prev = "" # 이전 문자
for i, char in enumerate(expression):
if char in "0123456789.":
num += char
elif char in "+-":
if i == 0 or prev in "+-*/": # 단항 부호
num += char
else:
if num:
tokens.append(num)
num = ""
tokens.append(char)
elif char in "*/":
if num:
tokens.append(num)
num = ""
tokens.append(char)
else:
raise ValueError(f"Invalid character '{char}' in expression.")
prev = char
if num:
tokens.append(num)
return tokens
# 연산 우선순위를 고려한 계산 함수
def calculate(tokens):
# 1단계: 곱셈(*)과 나눗셈(/) 먼저 처리
i = 0
while i < len(tokens):
if tokens[i] in ("*", "/"):
try:
left = float(tokens[i - 1])
right = float(tokens[i + 1])
if tokens[i] == "*":
result = multiply(left, right)
else:
result = divide(left, right)
tokens = tokens[:i - 1] + [str(result)] + tokens[i + 2:]
i -= 1
except (ValueError, ZeroDivisionError):
raise
else:
i += 1
# 2단계: 덧셈(+)과 뺄셈(-)
i = 0
while i < len(tokens):
if tokens[i] in ("+", "-"):
try:
left = float(tokens[i - 1])
right = float(tokens[i + 1])
if tokens[i] == "+":
result = add(left, right)
else:
result = subtract(left, right)
tokens = tokens[:i - 1] + [str(result)] + tokens[i + 2:]
i -= 1
except ValueError:
raise
else:
i += 1
return float(tokens[0])
# 메인 함수
def main():
try:
expression = input("Enter expression: ")
tokens = tokenizer(expression)
result = calculate(tokens)
print(f"Result: {result}")
except ZeroDivisionError:
print("Error: Division by zero.")
except Exception:
print("Invalid input.")
if __name__ == "__main__":
main()'Codyssey > AI선발과정' 카테고리의 다른 글
| 재귀를 이용해 괄호를 처리하고 후위연산을 사용하지 않는 괄호와 생략곱셉을 처리하는 계산기 파이썬 (5) | 2025.07.23 |
|---|---|
| ()를 포함하는 수식 계산기 파이썬 (0) | 2025.07.23 |
| 음수를 처리하고 사칙연산 우선순위로 계산하는 계산기 (0) | 2025.07.22 |
| 계산기 후위계산 () 없이 (0) | 2025.07.22 |
| 과정1.Q3 정규식을 이용한 2개 이상의 오퍼랜드의 파싱 (2) | 2025.07.18 |