공학용 계산기 엔진 만들기
예전에 미진하게 만들다가 말았던 계산기 만들기와 관련해서. 지난 번 글은 사실 스탠포드의 iOS 앱 개발 강의 내용을 바탕으로 만들었다. 물론 일부 내용을 좀 수정하기는 했는데. 그 강의에서는 스택에 숫자와 연산자를 넣는 형태로 동작했다. 예를 들면 3 + 5 를 계산하기 위해서는 3, enter, 5, enter, plus 의 순서로 값을 먼저 입력하고 연산자를 뒤에 입력하는 형태의 약간 이상한 방식으로 동작했다. 이는 사실 그 계산기 프로젝트를 계속 발전시켜 공학용 계산기로 만들기 위해서이다.
보통의 공학용 계산기는 ‘후위식’을 이용한 계산 방법을 사용한다. 우리가 흔히 사용하는 1 + 2와 같은 수식의 형태를 연산자가 가운데에 들어간다고 해서 중위식이라고 하는데 이를 12+ 라 쓰고 뒤에서부터 계산하는 방식을 후위식이라 한다. 중위식을 후위식으로 변환할 때 연산의 우선 순위를 고려하게 되므로, 곱하기/나누기가 더하기/빼기와 섞여있는 식이나 괄호가 들어있는 식도 후위식으로 전환하고 나면 뒤쪽에서부터 차근차근 계산해서 한 번에 계산할 수 있는 장점이 있다. 물론 후위식 자체는 사람이 계산하기에는 무척 불편한 형태의 수식 표현이지만 컴퓨터는 스택에서 연산자와 피연산자를 꺼내서 쉽게 처리할 수 있다.
문제는… 이 후위식으로 변환하는 과정이 꽤나 골치아프다는 게 문제. (연산 우선순위를 정의한 기준과 별도의 스택이 추가로 필요) 그래서 생각한게 사람이 계산하는 방식과 마찬가지로 (단순 무식하게) 각 항별로 버퍼에서 우선 계산하고, 다음 항을 계산해야 할 때 버퍼에 있는 값을 결과값에 더해 버리면 되지 않을까? 하는 것이었다.
즉 이를 정리하면 다음과 같다.
- 버퍼의 값은 1로 초기화한다. 버퍼에는 숫자값과 현재 항이 음수인지, 양수인지를 구분하는 플래그, 그리고 새로 버퍼에 넣는 값을 곱할 것인지, 나눌 것인지를 결정할 플래그. 이렇게 3개의 값이 필요하다.
- 곱하기 연산자가 수식에서 나오면 다음에 나오는 숫자는 버퍼에 들어가 버퍼값과 곱해진다. 버퍼의 계산방식을 곱하기로 변경한다.
- 나누기 연산자가 수식에서 나오면 다음에 나오는 숫자는 버퍼에 들어가면서 기존 버퍼값을 나눈다. 버퍼의 계산 방식을 나누기로 변경한다.
- 더하기 연산자가 수식에서 나오면 이는 하나의 항에 대한 처리가 끝나고 다음 항이 시작되기 때문에 버퍼에 계산된 값을 꺼내 결과 값에 더해준다. 다음 버퍼는 “더해지는” 항이므로 버퍼의 부호를 양수로 설정해놓고, 값을 초기화한다.
- 빼기 연산자가 수식에서 나오면 이 역시 하나의 항에 대한 처리가 끝났다. 더하기와 같이 처리하고 다음 항의 부호는 음수이므로 버퍼의 부호를 음수로 설정한다.
- 숫자값이 나올 때는 이 값을 버퍼에 집어 넣는다. 버퍼의 곱하기/나누기 플래그에 따라 이값이 버퍼에 들어갈 때 기존의 버퍼의 값에 곱하거나 나눈다.
이 규칙에 따라 곱하기나 나누기로 연결된 식은 하나의 항으로 취급되어 버퍼에서 계산되고, 더하기나 빼기가 나타날 때 결과 값에 더해진다. 이 과정을 수식의 전체에 대해 진행하면 자동으로 식이 계산된다.
이 방식을 사용하면 괄호처리 역시 어렵지 않게 할 수 있다. 여는 괄호가 나타나면 그 괄호를 닫을 때까지 (따라서 열린 괄호의 수를 카운트하며 추적해야 한다) 수식에 들어있는 식을 별도의 수식으로 만든다. 그런 다음 이 수식을 수식 처리 함수 (즉 그 함수 자신)에게 넘겨 재귀적으로 처리하고 그 결과값을 버퍼에 집어넣으면 된다.
기타 파이, 로그, 제곱, 제곱근 등의 처리도 버퍼에서 별도의 필터를 통해 기능을 추가해 나갈 수 있을 것이다.
계산기엔진 소스코드 : http://www.box.com/s/2neu29qfmblaeoiaj5a8