책에는 중위계산을 후위계산으로 바꾸는 법이 나와있는데

연습할겸 중위계산을 바로 결과로 보여주기로 했다.


코드가 점점 복잡해져서 작동한 뒤에는 좀더 서술적인 함수이름으로 정리해보았다.

느낀점은 일단 예전에 PPP C++에서 작성해본 구문분석형 계산기보다 기능추가가 훨씬 힘들고, 코드 읽는 것도 힘들다.


중위계산을 후위계산으로 바꿀 때 

중위계산은 연산자를 저장해두었다가 나중에 봐야하므로 숫자와 연산자를 위한 스택을 따로 준비했다.

1+2*4+1 를 진행해보자.


단계(0) : 1을 숫자스택에 담는다.

단계(1) : +을 연산스택에 담는다.

단계(2) : 2를 숫자스택에 담는다.

단계(3) : *를 읽었다. *를 연산스택의 top인 +와 비교한다. *의 순위는 +보다 높으므로 별일없이 스택에 담아둔다.

단계(4) : 4를 숫자스택에 담는다.

단계(5) : +를 읽었다. 단계(3)과 마찬가지로, top인 *과 비교한다. 이번엔 반대로 *가 높으므로 *의 연산을 먼저 진행한다.

가장 위에서부터 4와 2를 스택에서 빼고 2*4 = 8을 숫자 스택에 담는다. 연산스택의 pop으로 *를 제거한다.

단계(5)의 과정은 더이상 현재 연산자인 +보다 높거나 같은 연산자가 없을 때까지 계속 진행된다.

여기에서 다음 연산자는 처음에 담은 +다. +와 +는 같으므로 스택에 담긴 8과 1의 + 연산을 진행한다. 기존의 +는 pop.

이제 숫자 스택에는 9만, 연산 스택에는 새 +만 남았다.

단계(6) : 1을 숫자스택에 담는다.

단계(7) : 더이상 읽을 문자가 없다. 숫자 스택에 남은 1과 9, 연산 스택의 +로 마지막 계산을 한다. (이 과정은 더이상 연산자가 없을 때까지 반복된다.)

결과는 9 + 1인 10이 되었다.


괄호 처리

(는 우선순위가 제일 낮아 다음 연산자에 영향을 주지 않는다.

)는 (가 나올 때 까지 괄호안의 남은 연산을 진행한다. 괄호 외의 연산을 무시하는 효과가 있다.

2 * (3 + 4) - 4   는  2 3 4 + * 4 - 이 될 것이다.


이를 코드로 표현했더니 다음과 같다.



constexpr int arrSize = 32;


double calcul_infix_expression(FILE* fp = stdin)

{

char cCur{};

ArrayStack<double, arrSize > numStack;

ArrayStack<char, arrSize > opStack;


while ((cCur = getc(fp)) != '\n')

{

switch (cCur)

{

case '0': case '1':case '2':case '3':case '4':

case '5': case '6':case '7':case '8':case '9':

numStack.push(get_complte_number(cCur));

break;


case '+': case '-': case '*': case '/':

deal_operators(cCur, numStack, opStack);

break;


case '(': case ')':

deal_parentheses(cCur, numStack, opStack);

break;

}

}

calcul_stack_remains(cCur, numStack, opStack);


return numStack.top();

}


double get_complte_number(char cCur)

{

cin.putback(cCur);

double val{};

cin >> val;

return val;

}


void deal_operators(char token, ArrayStack<double, arrSize >& numStack, ArrayStack<char, arrSize >& opStack)

{

while (!opStack.isEmpty())

{

char topOp = opStack.top();

if (opPriority(topOp) >= opPriority(token))

{

push_calculated_result(topOp, numStack);

opStack.pop();

}

else break;

}

opStack.push(token);

}


int opPriority(char n)

{

switch (n)

{

case '(': case ')': return 0;

case '+': case '-': return 1;

case '*': case '/': return 2;

}

return -1;

}


void push_calculated_result(char topOp, ArrayStack<double, arrSize >& numStack)

{

double latter = numStack.peekAndPop();

double former = numStack.peekAndPop();

numStack.push(calcul_binary_Op(topOp, latter, former));

}


double calcul_binary_Op(char op, double latter, double former)

{

switch (op)

{

case '+': return former + latter;

case '-': return former - latter;

case '*': return former * latter;

case '/':

if (latter == 0) throw exception("divided by zero : calcul_binary_Op\n");

return former / latter;

}

throw exception(" No Return : calcul_binary_Op\n");

}


void deal_parentheses(char token, ArrayStack<double, arrSize >& numStack, ArrayStack<char, arrSize >& opStack)

{

if (token == '(')

{

opStack.push(token);

return;

}

else if (token == ')')

{

while (!opStack.isEmpty())

{

char topOp;

topOp = opStack.peekAndPop();

if (topOp == '(') break;

else

push_calculated_result(topOp, numStack);

}

}

}


void calcul_stack_remains(char token, ArrayStack<double, arrSize >& numStack, ArrayStack<char, arrSize >& opStack)

{

while (!opStack.isEmpty())

{

char topOp = opStack.top();

push_calculated_result(topOp, numStack);

opStack.pop();

}

}



int main()

{

while (true)

try{

cout << calcul_infix_expression() << endl;

}

catch (exception& e){

cout << e.what();

}

return 0;

}




'프로그래밍' 카테고리의 다른 글

구현 - 배열기반 덱  (0) 2018.03.04
구현 - 배열기반 원형 큐  (0) 2018.03.03
구현 - 스택 응용 문제 - 후위계산  (0) 2018.03.03
구현 - 배열기반 스택  (0) 2018.03.03
구현 - 싱글 링크드 리스트 ver2  (0) 2018.02.27