스택을 사용해 후위 표기법으로 변환하여 실수의 사칙연산을 계산
---- 지원 기능 ----
1. 괄호를 포함한 실수의 사칙연산
2. 0으로 나누거나 괄호의 개수가 맞지 않거나 사칙연산 외의 기호가 들어가는 등의 문법 오류 감지
3. 공백 무시
4. 부호를 표현하기 위한 +- 가능
---- 구현 코드 ----
main.cpp
/*
v1.0.0
[Last Update]: 2022.11.28
[File Name]: main.cpp
https://katteniiki.tistory.com/
*/
/*
InflixToPost: 중위->후위 convert, is_digit 전역함수(string의 숫자 판별)
PostCalculate: 후위 표기법 계산
Calculation: InflixToPost, PostCalculate의 함수 호출
expcetion: 예외 처리 클래스
cctype, stack, iostream, string, vector
*/
#include <iostream>
#include "Calculation.hpp"
int main()
{
Calculation calculation;
std::string str;
std::cout << "사칙연산 계산기입니다.\n실수의 사칙연산을 지원합니다\n"
<< "종료하려면 'q'를 입력하십시오.\n\n";
while (1)
{
std::cout << "[expression]: ";
std::getline(std::cin, str);
if (str.compare("q") == 0) break;
try
{
std::cout << "[ans]: " << calculation.Calculate(str) << "\n\n";
}
catch (std::exception& ex)
{
std::cout << ex.what() << "\n\n";
continue;
}
}
}
Calculation.hpp
/*
v1.0.0
[File Name]: Calculation.hpp
[Last Update]: 2022.11.28
https://katteniiki.tistory.com/
*/
#pragma once
#include "PostCalculate.h"
#include <iostream>
class Calculation
{
public:
double Calculate(const std::string& exp)
{
PostCalculate temp1;
InflixToPost temp2;
return temp1.calculate(temp2.convertPost(exp));
}
};
PostCalculate.h
/*
v1.0.0
[File Name]: PostCalculate.h
[Last Update]: 2022.11.28
https://katteniiki.tistory.com/
*/
#pragma once
#include "InflixToPost.h"
#include "Exception.hpp"
#include <iostream>
class PostCalculate
{
std::stack<double> stack;
public:
PostCalculate(); //초기화
//후위 표기법 계산 후 리턴
double calculate(const std::vector<std::string>& obj);
};
InflixToPost.h
/*
v1.0.0
[Last Update]: 2022.11.28
[File Name]: InflixToPost.h
https://katteniiki.tistory.com/
*/
#pragma once
#include <string>
#include <vector>
#include <stack>
#include <cctype>
#include "Exception.hpp"
bool is_digit(std::string str);
class InflixToPost
{
std::stack<char> stack; //연산자 저장
std::vector<std::string> postFix;
std::vector<char> expMemory;
//expMemory에 수식 저장
void saveExp(const std::string& copy);
void saveExp(const char* copy);
//연산자 우선순위
static int opPriority(const std::string op);
static int opPriority(const char op);
public:
//후위 표기법으로 변환 후 반환
const std::vector<std::string>& convertPost(const std::string& copy);
};
Exception.h
/*
v1.0.0
[File Name]: Exceotion.hpp
[Last Update]: 2022.11.28
https://katteniiki.tistory.com/
*/
#pragma once
#include <iostream>
#include <exception>
#include <string>
class badOperator :public std::exception //사칙 연산이 아닌 기호가 포함되었을 때
{
public:
virtual const char* what() const override
{
return "지원하지 않는 기호입니다.\n재입력을 요구합니다.";
}
};
class badExpression :public std::exception //수식이 잘못되어 계산이 불가능할 때
{
public:
virtual const char* what() const override
{
return "수식이 잘못된 것 같습니다.\n재입력을 요구합니다.";
}
};
class DevisionZero :public std::exception //0으로 나눈 경우
{
public:
virtual const char* what() const override
{
return "0로 나누는 연산이 시도되었습니다.\n재입력을 요구합니다.";
}
};
PostCalculate.cpp
/*
v1.0.0
[File Name]: PostCalculate.cpp
[Last Update]: 2022.11.28
https://katteniiki.tistory.com/
*/
#include "PostCalculate.h"
PostCalculate::PostCalculate()
{
while (!stack.empty()) stack.pop();
}
double PostCalculate::calculate(const std::vector<std::string>& obj)
{
while (!stack.empty()) stack.pop(); //스택 비우기
for (int i = 0; i < obj.size(); ++i)
{
if (is_digit(obj[i].c_str())) //숫자면 스택에 쌓는다
{
stack.push(stod(obj[i]));
}
else //연산자면 스택에서 숫자 두개를 꺼내서 연산한다
{
double op1, op2;
if (stack.empty()) throw badExpression();
op2 = stack.top();
stack.pop();
if (stack.empty()) throw badExpression();
op1 = stack.top();
stack.pop();
if (obj[i].compare("+") == 0) //+라면
{
stack.push(op1 + op2);
}
else if (obj[i].compare("-") == 0) //-라면
{
stack.push(op1 - op2);
}
else if (obj[i].compare("*") == 0) //*라면
{
stack.push(op1 * op2);
}
else if (obj[i].compare("/") == 0) // /라면
{
if (op2 == 0) throw DevisionZero();
stack.push(op1 / op2);
}
else //예외처리
{
throw badExpression();
}
}
}
if (stack.empty()) throw badExpression();
double result = stack.top();
stack.pop();
return result;
}
InflixToPost.cpp
/*
v1.0.0
[Last Update]: 2022.11.28
[File Name]: InflixToPost.cpp
https://katteniiki.tistory.com/
*/
#include "InflixToPost.h"
#include <iostream>
void InflixToPost::saveExp(const std::string& copy)
{
expMemory.clear();
for (int i = 0; i < copy.size(); ++i)
{
expMemory.push_back(copy[i]);
}
}
void InflixToPost::saveExp(const char* copy)
{
expMemory.clear();
for (int i = 0; copy[i] != NULL; ++i)
{
expMemory.push_back(copy[i]);
}
}
const std::vector<std::string>& InflixToPost::convertPost(const std::string& copy)
{
while (!stack.empty()) stack.pop(); //스택 비우기
postFix.clear(); //벡터 초기화
saveExp(copy);
for (int i = 0; i < expMemory.size();)
{
char expMemoryEle = expMemory.at(i);
if (expMemoryEle == ' ') //공백 무시
{
++i;
continue;
}
//하나의 숫자로 취급되어야 하는 경우
if (isdigit(expMemoryEle) || expMemoryEle == '.')
{
std::string temp;
do
{
temp.append(1, expMemory.at(i));
++i;
} while (i < expMemory.size() && (isdigit(expMemory.at(i)) || expMemory.at(i) == '.'));
postFix.push_back(temp);
}
else //연산자인 경우
{
switch (expMemoryEle)
{
case '(':
stack.push(expMemoryEle);
break;
case ')':
{ std::string temp;
while (1)
{
if (stack.empty()) throw badExpression();
if (stack.top() == '(')
{
stack.pop();
break;
}
temp = stack.top();
stack.pop();
postFix.push_back(temp);
}
break;
}
default:
//부호를 나타내기 위한 +-인 경우
if ((i == 0 || expMemory.at(i - 1) == '(') &&
(expMemoryEle == '-' || expMemoryEle=='+'))
{
postFix.push_back("0");
}
if (stack.empty()) stack.push(expMemoryEle);
else
{
while(opPriority(stack.top()) >= opPriority(expMemoryEle)) //우선순위 비교
{
//스택 top 원소의 우선순위가 더 높거나 같은 경우 스택에서 빼낸다.
std::string temp;
temp = stack.top();
stack.pop();
postFix.push_back(temp);
if (stack.empty()) break; //스택이 비면 break
}
stack.push(expMemoryEle);
}
}
++i;
}
}
std::string temp;
while(!stack.empty()) //스택에 남은 연산자 postFix로 옮기기
{
temp = stack.top();
stack.pop();
postFix.push_back(temp);
}
return postFix;
}
int InflixToPost::opPriority(const std::string op)
{
if (op.size() != 1) throw badExpression(); //예외처리
switch (op[0])
{
case '(':
return 1;
case '+':
case '-':
return 3;
case '*':
case '/':
return 5;
default:
throw badOperator(); //예외처리
}
}
int InflixToPost::opPriority(const char op)
{
switch (op)
{
case '(':
return 1;
case '+':
case '-':
return 3;
case '*':
case '/':
return 5;
default:
throw badOperator(); //예외처리
}
}
bool is_digit(std::string str) //string의 숫자 판별 전역함수
{
return atoi(str.c_str()) != 0 || str.compare("0") == 0;
}
---- 실행 예시 ----
'토이프로젝트' 카테고리의 다른 글
[테트리스][C++] 현재 상황 (0) | 2022.11.23 |
---|---|
[C++][테트리스] 대략적인 계획 (0) | 2022.11.15 |