본문 바로가기

뭐라도 만들어보자!!( 프로젝트 )

3. C++를 이용한 Postfix 계산기 만들기

반응형

 완성은 8월 6일에 했는 데, 미루다보니 16일인 지금에서야 올리게 되었다. 트리 구조를 배우고 있었던 당시 예제로 nTree를 구현하면서 이 것을 계산기를 만드는 데 사용할 수 도 있겠다는 생각이 들면서 코딩을 시작하게 되었다. 

 계산기는 기본적으로 콘솔 형식으로 사용자로부터 수식을 입력받으면, 계산기 앱이 이를 해석하고 결과를 도출하거나 혹은 식이 도저히 계산할 수 없을 정도로 엉망인 경우에는 에러 메세지를 뱉도록 하였다. 계산기 앱을 만드는 것은 생각보다는 난이도가 있었다. 특히 사용자의 입력 부분을 다루는 데 있어서, 생각할 부분들이 많았다. 예를 들어 정의되지 않은 연산 기호가 사용된 경우, 잘못된 형식의 수를 입력하거나(ex) 1..2), 연산 기호가 중복되어 입력된 경우 등에 대한 판단이나 수식 내의 공백 삭제, 음수 혹은 명시적인 양수를 파싱할 수 있도록 가공하는 등 생각보다 구현이 필요한 부분들이 많았다. 사실상 가장 메인 부분인 n-Tree base 계산기 클래스를 구현하는 것보다 더 많은 시간을 들인 것 같다.

 다시 본론으로 들어가서 계산기의 알고리즘에 대해서 설명하고자 한다. 계산기 클래스는 크게 3가지 기능을 갖추고 있다. 하나는 문자열을 노드 단위로 파싱하고 이를 트리에 추가하는 기능, 둘 째는 수식 노드의 자식 노드들을 Infix 에서 Postfix 로 Sorting 하는 기능, 마지막은 수식 노드에 대해서 만약 값이 확정되지 않았다면 Sorting된 자식 노드들을 이용하여 값을 확정 짓는 기능이다. 각 기능에 대해서 좀 더 자세하게 살펴보면 다음과 같다.

 

  • 파싱 및 노드 추가 : 각 노드는 데이터 셋, 이전 노드의 포인터, 다음 노드들의 포인터를 저장하는 벡터로 구성된다. 데이터 셋은 다시 노드의 타입(연산자, 피연산자, 수식), 연산자 종류(만약 연산자가 아닌 경우 -1), 피연산자의 값(미정되어 있거나, 피연산자가 아닌 경우 -1), 값이 확정되어 있는 지 여부(확정되지 않은 수식을 제외하고는 모두 true)로 구성되어 있다. 연산자의 경우 문자열의 연산자를 파싱하는 경우 해당 연산자를 저장한 연산자 타입의 노드를 현재 노드의 다음 노드 벡터에 Push-Back 해주었고, 피연산자의 경우 연산자와 비슷하게 해당 피연산자 값을 저장한 피연산자 타입의 노드를 현재 노드의 벡터에 Push-Back 해주었다. 다만 Open-Bucket(' ( ')을 받아 들인 경우는 조금 다른데, 이 경우에는 수식 노드를 추가하고, 현재 노드를 추가한 수식 노드로 변경해 주었다. 결론적으로 이후 괄호 안의 연산자 혹은 피연산자들은 괄호 전의 연산자, 피연산자보다 한 단계 높은 레벨에 저장된다. Close-Bucket(' ) ')의 경우 다시 이전 노드로 현재 노드의 위치를 변경시켜 주었다. 결과적으로 괄호 안에 싸매여 있을 수록 레벨이 높은 가지에 노드들이 저장되어 있는 구조다.
  • Infix to Postfix : 루트 노드부터 저장되어 있는 자식 노드들을 Postfix 로 Sorting 해 주었다. 우선 Sorting 한 결과를 저장할 벡터와 연산자 노드를 저장할 Stack을 선언해 준다. 기존의 벡터를 읽으면서 피연산자 노드가 있는 경우 결과 벡터에 Push-Back 해준다. 연산자 노드를 만난 경우,  Stack의 Top 에 읽은 연산자보다 우선순위가 높은 연산자 있는 지 확인한다. 만약  우선 순위가 높은 연산자가 있다면 Stack 이 빌 때까지 결과 벡터에 안에 있는 값들을 넣어주고 현재 연산자를 Stack에 넣어준다. 그렇지 않은 경우 그냥 Stack 현재 연산자를 Stack에 넣어주는 것으로 마무리한다. 수식 노드를 만난 경우 수식 노드 자체는 피연산자 노드 취급을 해준다. 다만 우선적으로 수식노드의 자식노드들을 재귀적으로 정렬하는 작업을 우선적으로 수행한다. 결과적으로 루트 노드 아래에 있는 모든 자식 노드들이 정렬되어 결과 벡터에 저장되어 있다면 기존의 벡터와 Replace 해주는 것으로 해당 작업을 마무리한다.
  •   계산 : 루트 노드는 기본적으로 미정된 수식노드이다. 계산의 결과는 루트노드에 저장되게 된다. 루트 노드의 자식 노드들이 정렬되어 있다는 가정하에 계산이 이루어진다. 자식 노드를 앞에서부터 읽어 피연산자의 경우 피연산자 값을 Stack에 저장하고, 연산자 노드가 나오는 경우 해당 연산자의 종류를 파악하여 2 개의 피연산자를 꺼내어 계산하고 이를 다시 Stack에 집어넣는 방식으로 최종적으로 모든 루트 노드의 자식 노드를 읽으면 Stack에 남는 단 하나의 값이 계산의 결과가 되어 루트 노드를 확정하는 것이 계산의 과정이다. 수식 노드를 만날 경우 기본적으로 피연산자 노드 취급을 해주나, 만약 이 노드가 값이 확정되지 않았다면 재귀적으로 해당 노드에 대한 계산이 이루어진 후에 확정된 값을 가지게 한다.

 만들고 나서, 다른 사람들이 짠 계산기 코드와 비교해보니 훨씬 복잡하다는 생각이 들었다. 다만 다른 사람들과 다르게 수식의 옳고 그름에 대한 판단이나 어느정도 수준에 편집하는 기능이 추가되었다는 점, 트리 구조를 이용하여 계산기 기능을 확장하기에는 구조적으로 훨씬 유리하다는 점에서 단순히 비효율적인 코드는 아니라는 생각이 들었다. 이번 프로젝트를 통해서 자료구조의 응용, 재귀 함수의 응용을 연습할 수 있었고, 사용자의 입력을 어떻게 처리할 지, 데이터 파싱을 어떻게 할 것인지 등 프로그램 설계에 대해 고민해보는 시간을 가질 수 있었다. 역시 백 날 이론으로 공부하는 것보다는 한 번 각 잡고 짜보는 것이 좋다는 생각이 들었다. 간단해도 좋으니 뭐라도 짜보는 습관은 역시 개발자를 꿈꾸는 이에게는 중요한 습관 같다.

 

짠 계산기 코드는 GitHub에서~

https://github.com/youngSeok0413/Calculator.git

 

GitHub - youngSeok0413/Calculator

Contribute to youngSeok0413/Calculator development by creating an account on GitHub.

github.com

 

같은 형식으로 자바에서 짠 코드(GUI 베이스)

https://github.com/youngSeok0413/Calculator-with-GUI-using-Java-.git

 

GitHub - youngSeok0413/Calculator-with-GUI-using-Java-

Contribute to youngSeok0413/Calculator-with-GUI-using-Java- development by creating an account on GitHub.

github.com

자바에서 짠 건 그냥 자바 공부하면서 익숙해질 겸 짠거라 편집 관련해서 몇 개는 기능이 누락되어 있습니다. 감안하고 봐 주세요~~

반응형