템플릿 template
- 클래스와 함수의 재사용에 기여
- 템플릿/ 인수화 타입을 이용해 소프트웨어 개발 시간과 비용 절약
SelectionSort
- 선택 정렬 기법을 이용해 n ≥1 개의 서로 다른 T의 집합을 정렬
- 사용 시 주의사항
- 사용자 정의 데이타 타입에 대해서는 별도로 정의해야 한다.
template <class T> // 이 부분을 반드시 넣어야 한다.
void SelectionSort(T *a, const int n) {
// n개의 T a[0]부터 a[n-1]까지 비감소 순으로 정렬한다.
for ( int i = 0; i < n; i++) {
int j = i;
// a[i]와 a[n-1] 사이에 가장 작은 T를 찾는다
for (int k = i + 1; k < n; k++)
if (a[k] < a[j]) j = k;
swap(a[i], a[j]);
}
}
ChangeSize1D
- 1-dim array의 크기 변경하는 템플릿 함수
ㅠtemplate <class T>
void ChangeSize1D(T *& a, const int oldSize, const int newSize) {
if (newSize < 0) throw “New length must be >= 0”;
T* temp = new T[newSize]; // 새로운 배열
int number = min(oldSize, newSize); // 복사할 원소 수
copy(a, a + number, temp);
delete [] a; // 이전 메모리를 제거
a = temp;
}
컨테이너 클래스
- 다수의 데이타 객체들을 수용 또는 저장하는 자료 구조를표현하는 클래스
백 Bag
- 동일한 원소가 여러 번 나타남
- 원소 위치, 삭제 연산 시 어떤 원소가 제거되는지는 문제 안됨
- 삽입(Push): 해당 원소를 배열의 첫번째 가용 위치에 저장. 배열이만원인 경우 그 크기를 두 배 확장.
- 삭제(Pop): 배열 중앙 위치 원소 삭제. 그 오른쪽 모든 원소는 한자리씩 왼쪽으로 이동.
- Element: 삭제될 원소를 반환
- 구현
- template <class T> class Bag { public: Bag (int bagCapacity = 10); ~Bag(); int Size() const; bool IsEmpty() const; T& Element() const; // element한개 반환 void Push(const T&); void Pop(); private: T *array; int capacity; int top; }
스택 Stack
💡 top에서 모든 삽입과 삭제가 일어나는 순서 리스트
- 후입 선출 LIFO 리스트
- 구현
Template <class T>
Class Stack { //0개 이상의 원소를 가진 유한 순서 리스트.
Public:
Stack(int stackCapacity = 10); // 처음크기 stackCapacity 공백 스택 생성
bool IsEmpty() const; // 스택 원소 수가 0이면 true, 아니면 false를 반환
T& Top() const; //스택의 톱 원소를 반환
void Push(const T& item) ; // 스택의 톱에 item을 삽입
void Pop(); // 스택의 톱 원소를 삭제
private:
T *stack; // 스택 원소를 위한 배열
int top; // 톱 원소의 위치
int capacity; // 스택 배열의 크기
};
template <class T>
Stack<T>::Stack (int stackCapacity): capacity (stackCapacity) {
if(capacity < 1) throw “Stack capacity must be > 0”;
stack = new T[capacity];
top = -1;
}
template <class T>
inline bool Stack<T>::IsEmpty() const { return top == -1; }
template <class T>
inline T& Stack<T>::Top() const {
if (IsEmpty()) throw “Stack is empty”;
return stack[top];
}
template <class T>
void Stack<T>::Push(const T& x) { // Add x to the stack
if (top == capacity – 1) {
ChangeSize1D(stack, capacity, 2*capacity);
capacity *= 2;
}
stack[++top] = x;
}
template <class T>
void Stack<T>::Pop() { // Delete top element from the stack.
if (IsEmpty()) throw “Stack is empty. Cannot delete.”;
stack[top--].~T(); // destructor for T
}
시스템 스택
- 프로그램 실행 시 함수 호출을 처리
- 함수 호출 시 스택 프레임 구조 또는 활성 레코드를 생성해 시스탬 스택의 top에 둔다.
- 이전의 스택 프레임에 대한 포인터
- 복귀 주소
- 지역 변수
- 매개 변수
서브 타입
- 상속 inheritance : 추상 데이타 타입간의 서브타입(subtype)관계를 표현
- IS-A(~의 하나이다) 관계
- Stack은 Bag의 서브타입
- 백(bag): 단순히 원소들이 삽입과 삭제가 될 수 있는 자료구조 -기본 클래스
- 스택: 원소들이 후입선출 순서에 따라 삭제되는 자료 구조(제한적) - 파생 클래스
- 상속의 의미
- 파생 클래스(Stack)가 기본 클래스(Bag)의 모든 멤버(데이타와 함수)들을 상속받음
- 단, 기본 클래스의 private 멤버는 접근 불가능
- 상속된 멤버들은 기본 클래스에서 가졌던 것과 똑같은접근 레벨을 파생클래스에서도 가짐
- 상속 받은 멤버함수는 똑같은 프로토 타입(prototype)을 유지함
- 파생 클래스(Stack)가 기본 클래스(Bag)의 모든 멤버(데이타와 함수)들을 상속받음
실습 코드
bagstack.h
using namespace std;
class Bag {
public:
Bag(int bagCapacity = 10); // 생성자
virtual ~Bag(); // 파괴자
virtual int Size() const; // 백에 있는 원소 수를 반환
virtual bool IsEmpty() const; // 백이 공백이면 true, 아니면 false를 반환
virtual int Element() const; // 백에 있는 원소를 반환
virtual void Push(const int);
virtual void Pop();
friend ostream& operator<<(ostream&, Bag&);
protected:
int* array;
int capacity; // 배열의 크기
int top; // 톱 원소의 배열 위치
};
class Stack : public Bag {
public:
Stack(int stackCapacity = 10);
~Stack();
int Top() const;
void Pop();
};
void ChangeSize1D(int*& a, const int oldsize, const int newSize);
bagstack.cpp
#include <algorithm>
#include <iostream>
using namespace std;
#include "bagstack.h"
Bag::Bag(int bagCapacity) : capacity(bagCapacity) {
if (capacity < 1) throw "Capacity must be > 0";
array = new int[capacity];
top = -1;
}
Bag::~Bag() { delete [] array; }
// 함수 호출의 오버헤드를 제거하기 위해 짧은 길이 코드는 inline으로 선언
// 백에 있는 원소 수를 반환
inline int Bag::Size() const { return top + 1; }
// 백이 공백이면 true, 아니면 false를 반환
inline bool Bag::IsEmpty() const { return Size() == 0; }
// 백에 있는 첫 번째 원소를 반환
inline int Bag::Element() const {
if (IsEmpty()) throw "Bag is empty";
return array[0];
}
// 백에 정수를 삽입
// T가 대형 객체일 때 생기는 부하를 막기위해 매개변수를 상수 참조함
// void Bag::Push(const T & x)
void Bag::Push(const int x) {
// 할당된 메모리가 꽉 찼으면 용량을 2배로 늘림
if (capacity == top + 1) {
ChangeSize1D(array, capacity, 2 * capacity);
capacity *= 2;
}
// 전위 연산 주의!
array[++top] = x;
}
void ChangeSize1D(int*& a, const int oldsize, const int newSize) {
if (newSize < 0) throw "New length must be >= 0";
// 새로운 배열
int* temp = new int[newSize];
// 복사할 원소 수
int number = std::min(oldsize, newSize);
copy(a, a + number, temp);
// 이전 메모리를 제거
delete[] a;
a = temp;
}
void Bag::Pop() {
if (IsEmpty()) throw "Bag is empty, cannot delete";
int deletePos = top / 2;
// 배열 정리 - 가운데 원소를 삭제한다.
copy(array + deletePos + 1, array + top + 1, array + deletePos);
top--;
// array[top--].~T();
}
Stack::Stack(int stackCapacity) :Bag(stackCapacity) {}
Stack::~Stack() { }
int Stack::Top() const {
if (IsEmpty()) throw "Stack is empty";
return array[top];
}
void Stack::Pop() {
if (IsEmpty()) throw "Stack is empty. Cannot delete";
top--;
}
ostream& operator<<(ostream& os, Bag& b) {
for (int i = 0; i <= b.top; i++)
os << b.array[i] << " ";
os << endl;
return os;
}
#include <iostream>
using namespace std;
#include "bagstack.h"
int main() {
Bag b(3);
Stack s(3);
b.Push(1); b.Push(2); b.Push(3); b.Push(4); b.Push(5);
cout << "Bag: " << b ;
// Bag: 1 2 3 4 5
s.Push(1); s.Push(2); s.Push(3); s.Push(4); s.Push(5);
cout << "Stack: " << s <<endl ;
// Stack: 1 2 3 4 5
b.Pop(); s.Pop();
cout << "Bag: "<< b ;
cout << "Stack: " << s << endl<<endl;
// Bag: 1 2 4 5
// Stack: 1 2 3 4
int j = s.Size(); //Stack의 부모클래스 Bag의 Size()가 불려진다
cout << "Stack 크기: " << j << endl;
j = s.Top(); //Stack의 Top()가 불려진다
cout << "Stack Top() 결과: " << j << endl;
j = s.Element(); //Stack의 부모클래스 Bag의 Element()가 불려진다
cout << "Stack Element() 결과: " << j << endl<< endl;
// Stack 크기: 4
// Stack Top() 결과: 4
// Stack Element() 결과: 1
}
큐 queue
💡 rear에서 삽입이 일어나고 front에서 삭제가 일어나는 순서 리스트
- 새로운 원소가 삽입되는 끝 - rear
- 원소가 삭제되는 끝 - front
- 선업선출 FIFO 리스트
- 구현
- 첫 번째 원소가 queue[0]일 때
- 삭제(pop) : queue[0]에 있는 앞 원소를 제거
- 삭제할 때마다 나머지 원소들을 왼쪽으로 이동해야 함
- 큐가 n개의 원소를 가질 때, 삭제 시 Θ(n) 시간이 걸림
- 삽입(push) : 배열 크기 조절 시간을 제외하면 Θ(1) 시간이 걸림
- 삭제(pop) : queue[0]에 있는 앞 원소를 제거
- 첫 번째 원소가 queue[front]일 때
- 변수 front를 사용하여 큐의 첫번째 위치를 항상 유지
- 원소를 삭제해도 다른 원소들을 이동할 필요가 없음
- 삭제를 Θ(1) 시간에 수행 가능
- 첫 번째 원소가 queue[0]일 때
원형 큐 Circular queue
- queue[front]로 표현한 큐에서 rear가 capacity-1과 같고, front > 0 일 때 배열의 크기를 확장해야한다.
- 이때 큐가 배열의 끝에서 되돌아오게 하면 큐에 저장되는 원소들은 모두 시계 방향으로 저장된다.
- capacity-1의 다음 위치 = 0
- 0의 직전 위치 = capacity-1
- 변수 front는 큐에서 첫 원소의 위치보다 반시계방향으로 하나 앞위치를 가리킴
- 변수 rear는 큐에서 마지막 원소의 위치를 가리킴
- 구현
private:
T* queue; // 큐 원소를 위한 배열 (heap영역에 위치)
int front, // 첫번째 원소로부터 반시계 방향으로 한 위치 뒤
rear, // 마지막 원소의 위치
capacity; // 현재 확보된 큐 배열의 크기
template <class T>
Queue<T>::Queue (int queueCapacity): capacity (queueCapacity) {
if(capacity < 1) throw “Queue capacity must be > 0”;
// front == rear가 공백인지 포화인지 구분하기 위해
// 최대 원소 수를capacity가 아니라 capacity-1로 한다
queue = new T[capacity];
front = rear = 0;
}
// 큐의 공백 조건 : front == rear
template <class T>
inline bool Queue<T>::IsEmpty() { return front == rear; }
Template <class T>
Inline T& Queue<T>::Front() {
if(IsEmpty()) throw “Queue is empty. No front element”;
return queue[(front+1)%capacity];
}
Template <class T>
Inline T& Queue<T>::Rear() {
if(IsEmpty()) throw “Queue is empty. No rear element”;
return queue[rear];
}
- push 구현
- 크기가 두배 되는 새로운 배열 newQueue를 생성한다.
- 두번째 부분(즉, queue[front+1]과 queue[capacity-1] 사이에 있는 원소들)을 newQueue의 0에서부터 복사해 넣는다.
- 첫번째 부분(즉 queue[0]와 queue[rear]사이에 있는 원소들)을 newQueue의 capacity-front-1에서부터 복사해 넣는다
template <class T> void Queue<T>::Push(const& x) {// add x at rear of queue. if ((rear + 1) % capacity == front) { // queue capacity 2배 확장 코드 T* newQueue = new T[2*capacity]; // copy from queue to new Queue int start = (front+1) % capacity; if (start < 2) // no wrap around copy(queue+start, queue+start+capacity-1, newQueue); else { copy(queue+start, queue+capacity, newQueue); copy(queue, queue+rear+1, newQueue+capacity-start); } // switch to newQueue front = 2*capacity – 1; rear = capacity-2; capacity *= 2; delete [] queue; queue = newQueue; } rear = (rear+1)%capacity; queue[rear] =x; }
- pop 구현
template <class T>
void Queue<T>::Pop() {// 큐로부터 원소를 삭제한다.
if(isEmpty()) throw “Queue is empty. Cannot delete.”;
front = (front+1)%capacity;
queue[front].~T(); // T를 위한 파괴자
}
미로 maze
- 1≤i≤m이고 1≤j≤p인 이차원 배열 maze[m][p]로 표현
- 1 : 통로가 막혀 있음, 0 : 통과할 수 있음
- 경계선에 있는 경우 : i ==1, i == m , j==1, j == p 인 경우
- 경계 조건을 매번 검사하지 않기 위해 미로의 주위를 1로 둘러쌈
- 배열은 maze[m+2][p+2]로 선언
- 이동 방향 미리 정의하는 방법
- 구현
maze.cpp
#include <iostream>
#include <stack>
using namespace std;
const int MAXSIZE = 100;
bool maze[MAXSIZE + 2][MAXSIZE + 2];
bool mark[MAXSIZE + 1][MAXSIZE + 1] = { 0 };
enum directions { N, NE, E, SE, S, SW, W, NW };
struct offsets {
int a, b;
}
movea[8] = { -1,0, -1,1, 0,1, 1,1, 1,0, 1,-1, 0,-1, -1,-1 };
struct Items {
Items(int xx = 0, int yy = 0, int dd = 0) : x(xx), y(yy), dir(dd) {}
int x, y, dir; // dir은 다음에 방문 예정인 방향
};
template <class T>
ostream& operator<< (ostream& os, stack<T>& s) {
// 스택의 내용을 역순으로 출력
stack<T> s2;
// 내용을 하나씩 꺼내 다른 임시 스택에 넣어 저장한 후,
while (!s.empty()) { s2.push(s.top()); s.pop(); }
// 그 임시 스택에서 하나씩 꺼내 출력
while (!s2.empty()) {
os << " -> " << s2.top();
s.push(s2.top()); s2.pop();
}
return os;
}
ostream& operator<<(ostream& os, Items& item) {
// 5개의 Items가 출력될 때마다 줄 바꾸기 위한 변수
static int count = 0;
os << "(" << item.x << "," << item.y << ")";
count++;
if ((count % 5) == 0) cout << endl;
return os;
}
void Path(const int m, const int p) {
mark[1][1] = 1;
// C++ STD stack을 이용하기
stack<Items> stack;
// (1,1)에서 E방향부터(시계방향) 순차적으로 시도함
Items temp(1, 1, E);
stack.push(temp);
while (!stack.empty()) {
temp = stack.top(); stack.pop();
// (i,j)부터 (g,h)로 이동한다
int i = temp.x; int j = temp.y; int d = temp.dir;
while (d < 8) {
int g = i + movea[d].a; int h = j + movea[d].b;
if ((g == m) && (h == p)) { // 출구에 도착하면
// 경로를 출력한다.
cout << stack;
// 경로 상의 마지막 두 위치
temp.x = i; temp.y = j; cout << "->" << temp;
temp.x = m; temp.y = p; cout << "->" << temp << endl;
return;
}
// 방문하지 않은 새로운 위치라면
if ((!maze[g][h]) && (!mark[g][h])) {
// 방문했다고 표시한다.
mark[g][h] = 1;
// 현 위치와 실패 시 다음에 시도할 방향을 저장한다.
temp.x = i; temp.y = j; temp.dir = d + 1;
stack.push(temp);
// (g, h)로 이동한다. N방향부터 시계방향으로 시도하자.
i = g; j = h; d = N;
}
else d++;
}
}
cout << "No path in maze." << endl;
}
void getdata(istream& is, int& m, int& p) {
// 자료화일을 읽어들여 maze에 저장한다
is >> m >> p;
for (int i = 0; i < m + 2; i++) { maze[i][0] = 1; maze[i][p + 1] = 1; }
for (int j = 1; j <= p; j++) { maze[0][j] = 1; maze[m + 1][j] = 1; }
for (int i = 1; i <= m; i++)
for (int j = 1; j <= p; j++)
is >> maze[i][j];
}
#include <iostream>
#include <fstream>
using namespace std;
void getdata(istream&, int&, int&);
void Path(int, int);
int main(int argc, char* argv[]) {
int m, p; // m by p maze
if (argc == 1)
cerr << "Usage: " << argv[0] << " maze_data_file" << endl;
else {
ifstream is(argv[1]);
if (!is) { cerr << argv[1] << " does not exist\\n"; exit(1); }
cout << "For maze datafile (" << argv[1] << ")\\n";
getdata(is, m, p); is.close();
Path(m, p);
}
}
12 15
0 1 0 0 0 1 1 0 0 0 1 1 1 1 1
1 0 0 0 1 1 0 1 1 1 0 0 1 1 1
0 1 1 0 0 0 0 1 1 1 1 0 0 1 1
1 1 0 1 1 1 1 0 1 1 0 1 1 0 0
1 1 0 1 0 0 1 0 1 1 1 1 1 1 1
0 0 1 1 0 1 1 1 0 1 0 0 1 0 1
0 0 1 1 0 1 1 1 0 1 0 0 1 0 1
0 1 1 1 1 0 0 1 1 1 1 1 1 1 1
0 0 1 1 0 1 1 0 1 1 1 1 1 0 1
1 1 0 0 0 1 1 0 1 1 0 0 0 0 0
0 0 1 1 1 1 1 0 0 0 1 1 1 1 0
0 1 0 0 1 1 1 1 1 0 1 1 1 1 0
For maze datafile (mazein.txt)
-> (1,1) -> (2,2) -> (1,3) -> (1,4) -> (1,5)
-> (2,4) -> (3,5) -> (3,4) -> (4,3) -> (5,3)
-> (6,2) -> (7,2) -> (8,1) -> (9,2) -> (10,3)
-> (10,4) -> (9,5) -> (8,6) -> (8,7) -> (9,8)
-> (10,8) -> (11,9) -> (11,10) -> (10,11) -> (10,12)
-> (10,13) -> (9,14) -> (10,15)->(11,15)->(12,15)
후위 표기식
- 장점
- 괄호가 불필요
- 연산자의 우선 순위가 무의미
- 계산 과정이 간단 (왼편에서 오른편으로 스캔)
- 중위 표기식 → 후위 표기식 변환 알고리즘
- 식을 전부 괄호로 묶는다
- 이항 연산자들을 모두 자기 오른쪽 괄호로 이동시킨다.
- 괄호를 전부 삭제한다.
- 연산자 우선 순위
- 우선순위를 기반으로 스택에 push와 pop을 수행
- ‘(‘ 의 우선순위가 문제임
- 스택에 들어가기 전에는 가장 높은 우선순위의 연산자
- 스택 속에 있을 때는 ‘)’가 나타날 때까지 낮은 우선순위의 연산자
- so, 스택 top의 연산자의 isp가 새로운 연산자의 icp보다 산술적으로 작거나같을 때 스택에서 pop되어 출력
- for (; isp(stack.Top()) <= icp(x); stack.Pop())
- 예시
- 구현
- Expression e에 대한 NextToken(e)에서 unary – 와 binary –를 어떻게 구별하는가?
- Infix Notation으로 된 Expression인 경우 Operand(피연산자) 후에 나온 –는 binary –이고, Operator(연산자) 후에 나온 –는 unary –이다. 단 맨 처음에 만난 –는 unary-로 간주한다.
- Postfix Notation으로 된 Expression인 경우 입력자체가 binary –(-)와 unary –(-u)로 구별해야 함.
- Expression e에 대한 NextToken(e)에서 unary – 와 binary –를 어떻게 구별하는가?
post.cpp
#include <iostream>
#include <stack>
#include "post.h"
using namespace std;
bool Token::operator==(char b) { return len == 1 && str[0] == b; }
bool Token::operator!=(char b) { return len != 1 || str[0] != b; }
Token::Token() {}
// 1-char token: type equals the token itself
Token::Token(char c) : len(1), type(c) {
str = new char[1]; str[0] = c;
} // default type = c itself
// 2-char token(e.g. <=) & its type(e.g. LE)
Token::Token(char c, char c2, int ty) : len(2), type(ty) {
str = new char[2]; str[0] = c; str[1] = c2;
}
//operand with its length & type(defaulted to ID)
Token::Token(char* arr, int l, int ty = ID) : len(l), type(ty) {
str = new char[len + 1];
for (int i = 0; i < len; i++) str[i] = arr[i];
str[len] = '\\0';
if (type == NUM) {
ival = arr[0] - '0';
for (int i = 1; i < len; i++) ival = ival * 10 + arr[i] - '0';
}
else if (type == ID) ival = 0;
else throw "must be ID or NUM";
}
// 피연산자(ID 또는 NUM)인지 확인
bool Token::IsOperand() {
return type == ID || type == NUM;
}
ostream& operator<<(ostream& os, Token t) {
if (t.type == UMINUS) os << "-u";
else if (t.type == NUM) os << t.ival;
else for (int i = 0; i < t.len; i++) os << t.str[i];
os << " ";
return os;
}
bool GetID(Expression& e, Token& tok) {
char arr[MAXLEN]; // ID 저장할 배열
int idlen = 0;
char c = e.str[e.pos];
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) return false;
arr[idlen++] = c;
e.pos++;
while ((c = e.str[e.pos]) >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') {
arr[idlen++] = c; e.pos++;
}
arr[idlen] = '\\0';
tok = Token(arr, idlen, ID);
return true;
}
bool GetInt(Expression& e, Token& tok) {
char arr[MAXLEN];
int idlen = 0;
char c = e.str[e.pos];
if (!(c >= '0' && c <= '9')) return false;
arr[idlen++] = c;
e.pos++;
while ((c = e.str[e.pos]) >= '0' && c <= '9') {
arr[idlen++] = c; e.pos++;
}
arr[idlen] = '\\0';
tok = Token(arr, idlen, NUM);
return true;
}
void SkipBlanks(Expression& e) {
char c;
while (e.pos < e.len && ((c = e.str[e.pos]) == ' ' || c == '\\t'))
e.pos++;
}
// 두 글자 토큰들 처리: <= >= == != && || -u
bool TwoCharOp(Expression& e, Token& tok) {
char c = e.str[e.pos]; char c2 = e.str[e.pos + 1];
int op; // LE GE EQ NE AND OR UMINUS
if (c == '<' && c2 == '=') op = LE;
else if (c == '>' && c2 == '=') op = GE;
else if (c == '=' && c2 == '=') op = EQ;
else if (c == '!' && c2 == '=') op = NE;
else if (c == '&' && c2 == '&') op = AND;
else if (c == '|' && c2 == '|') op = OR;
else if (c == '-' && c2 == 'u') op = UMINUS;
else
return false;
tok = Token(c, c2, op); e.pos += 2;
return true;
}
// 한 글자 토큰들 처리
bool OneCharOp(Expression& e, Token& tok) {
char c = e.str[e.pos];
if (c == '-' || c == '!' || c == '*' || c == '/' || c == '%' ||
c == '+' || c == '<' || c == '>' || c == '(' || c == ')' || c == '=') {
tok = Token(c); e.pos++;
return true;
}
return false;
}
// 2nd arg=true for infix expression
Token NextToken(Expression& e, bool INFIX = true) {
// 종전에 연산자 발견되었다고 가정
static bool oprrFound = true;
Token tok;
// 빈칸이 있다면 무시
SkipBlanks(e);
// 더 이상 남은 토큰이 없는 경우
if (e.pos == e.len)
{ if (INFIX) oprrFound = true; return Token('#'); }
if (GetID(e, tok) || GetInt(e, tok))
{ if (INFIX) oprrFound = false; return tok; }
if (TwoCharOp(e, tok) || OneCharOp(e, tok)) {
// 연산자 다음에 -가 나온 경우 -u로 바꿔라
if (tok.type == '-' && INFIX && oprrFound)
tok = Token('-', 'u', UMINUS);
if (INFIX) oprrFound = true; return tok;
}
throw "Illegal Character Found";
}
// in-coming priority
int icp(Token& t) {
int ty = t.type;
int ty_num = -1;
// ty가 '('면, 0, UMiNUS나 '!'면 1,'*'나 '/'나 '%'aus 2, '+'나 '-'면 3, '<'나 '>'나 LE나 GE면 4,
// EQ나 NE면 5, AND면 6, OR이면 7, '='이면 8,'#'면 9를 return 한다.
if (ty == '(') ty_num = 0;
else if (ty == UMINUS || ty == '!') ty_num = 1;
else if (ty == '*' || ty == '/' || ty == '%') ty_num = 2;
else if (ty == '+' || ty == '-') ty_num = 3;
else if (ty == '<' || ty == '>' || ty == LE || ty == GE) ty_num = 4;
else if (ty == EQ || ty == NE) ty_num = 5;
else if (ty == AND) ty_num = 6;
else if (ty == OR) ty_num = 7;
else if (ty == '=') ty_num = 8;
else if (ty == '#') ty_num = 9;
else ty_num = -1;
return ty_num;
}
//in-stack priority
int isp(Token& t) {
int ty = t.type;
int ty_num = -1;
// icp에서 (랑 #만 바뀜
if (ty == '(') ty_num = 9;
else if (ty == UMINUS || ty == '!') ty_num = 1;
else if (ty == '*' || ty == '/' || ty == '%') ty_num = 2;
else if (ty == '+' || ty == '-') ty_num = 3;
else if (ty == '<' || ty == '>' || ty == LE || ty == GE) ty_num = 4;
else if (ty == EQ || ty == NE) ty_num = 5;
else if (ty == AND) ty_num = 6;
else if (ty == OR) ty_num = 7;
else if (ty == '=') ty_num = 8;
else if (ty == '#') ty_num = 10;
else ty_num = -1;
return ty_num;
}
// 중위 표기식 e의 후위 표기식을 출력한다.
void Postfix(Expression e) {
// 스택 초기화
stack<Token> stack;
// End Of String 표시
stack.push('#');
for (Token x = NextToken(e); x != '#'; x = NextToken(e))
if (x.IsOperand()) cout << x;
else if (x == ')') {
// '('까지 스택에서 꺼낸다.
for (; stack.top() != '('; stack.pop())
cout << stack.top();
// 'C'를 제거한다.
stack.pop();
} else { // x가 연산자일 떄
for (; isp(stack.top()) <= icp(x); stack.pop()) {
// 예외처리: a = b = 0 같은 우결합 연산자에선
// 나중에 나온 = 이 우선순위가 높음.
if (x == '=' && stack.top() == '=') break;
cout << stack.top();
}
stack.push(x);
}
// 마지막 '#'의 출력은 생략한다.
while (stack.top() != '#') { cout << stack.top(); stack.pop(); }
stack.pop();
cout << endl;
}
post.h
#ifndef POST_H
#define POST_H
// token types for non one-char tokens
#define ID 257
#define NUM 258
#define EQ 259
#define NE 260
#define GE 261
#define LE 262
#define AND 263
#define OR 264
#define UMINUS 265
#define MAXLEN 80
struct Expression {
Expression(char* s) : str(s), pos(0) { for (len = 0; str[len] != '\\0'; len++); }
char* str;
int pos;
int len;
};
struct Token {
bool operator==(char);
bool operator!=(char);
Token();
Token(char); // 1-char token: type equals the token itself
Token(char, char, int); // 2-char token(e.g. <=) & its type(e.g. LE)
Token(char*, int, int); //operand with its length & type(defaulted to ID)
bool IsOperand(); // true if the token type is ID or NUM
int type; // ascii code for 1-char op; predefined for other tokens
char* str; // token value
int len; // length of str
int ival; // used to store an integer for type NUM; init to 0 for ID
};
using namespace std;
ostream& operator<<(ostream& os, Token t);
Token NextToken(Expression&, bool); // 2nd arg=true for infix expression
#endif
A * B * C
-A + B - C + D
A * -B + C
(A + B) * D + E / (F + A * D) + C
A B * C *
A -u B + C - D +
A B -u * C +
A B + D * E F A D * + / + C +
A * B * C
-A + B - C + D
A * -B + C
(A + B) * D + E / (F + A * D) + C
A B * C *
A -u B + C - D +
A B -u * C +
A B + D * E F A D * + / + C +
A && B||C||!(E>F)
!(A&&!((B<C)||(C>D)))||(C<E)
A B && C || E F > ! ||
A B C < C D > || ! && ! C E < ||
34 * 56 + 11 / 2
1 + 2 * 3 + - 4 * 2s
34 56 * 11 2 / +
1 2 3 * + 4 -u 2 s * +
33 + 55 * 2
an77 = 2 + 7 * 5
b = 2
an77 + b * 2
a + 5
33 55 2 * +
an77 2 7 5 * + =
b 2 =
an77 b 2 * +
a 5 +
eval.cpp
#include <iostream>
#include <stack>
#include "post.h"
using namespace std;
int GetVal(Token& opnd) {
if (opnd.type == NUM) return opnd.ival;
return 0; // ID인 경우는 0을 return
}
// UMINUS와 ‘!’의 두가지 경우만 처리함
Token UnaryOp(int op, Token& x) {
if (!x.IsOperand()) throw "Operand Only for Unary Operator";
Token tmp; tmp.type = NUM;
if (op == UMINUS) tmp.ival = -GetVal(x);
else if (op == '!') tmp.ival = !GetVal(x);
else throw "Invalid unary operator";
return tmp;
}
Token BinaryOp(int op, Token& left, Token& right) {
if (!left.IsOperand() || !right.IsOperand())
throw "Two Operands required for Binary Operation";
Token tmp; tmp.type = NUM;
int val1 = GetVal(left), val2 = GetVal(right);
if (op == '+') tmp.ival = val1 + val2;
else if (op == '-') tmp.ival = val1 - val2;
else if (op == '*') tmp.ival = val1 * val2;
else if (op == '/') tmp.ival = val1 / val2;
else if (op == '%') tmp.ival = val1 % val2;
else if (op == '<') tmp.ival = val1 < val2;
else if (op == '>') tmp.ival = val1 > val2;
else if (op == GE) tmp.ival = val1 >= val2;
else if (op == LE) tmp.ival = val1 <= val2;
else if (op == EQ) tmp.ival = val1 == val2;
else if (op == NE) tmp.ival = val1 != val2;
else if (op == NE) tmp.ival = val1 && val2;
else if (op == NE) tmp.ival = val1 || val2;
else if (op == '=') throw "Assignment Not Yet Implemented";
else throw "No such binary operator";
return tmp;
}
// 스택의 top에 있는 것을 꺼내어 return한다.
Token DeleteFromStack(stack<Token>& stack) {
if (stack.empty()) throw "Trying to pop from an empty stack";
Token tmp = stack.top();
stack.pop();
return tmp;
}
// postfix 표현식을 입력으로 받아 그 값을 계산한다.
void Eval(Expression e) {
stack<Token> stack;
Token opnd1, opnd2;
for (Token x = NextToken(e, false); x != '#'; x = NextToken(e, false))
if (x.IsOperand()) stack.push(x);
else { // x is an operator
// unary operator
if (x.type == UMINUS || x.type == '!') {
opnd1 = DeleteFromStack(stack);
stack.push(UnaryOp(x.type, opnd1));
}
else { // binary operator
opnd1 = DeleteFromStack(stack);
opnd2 = DeleteFromStack(stack);
stack.push(BinaryOp(x.type, opnd2, opnd1));
}
}
// 최종 계산 결과 출력
cout << DeleteFromStack(stack) << endl;
}
posteval.cpp
#include <iostream>
#include "post.h"
using namespace std;
void Eval(Expression);
int main(void)
{
char line[MAXLEN];
while (cin.getline(line, MAXLEN)) {
Expression e(line); // Assume postfix notation
try {
Eval(e);
} catch (char const* msg) {
cerr << "Exception: " << msg << endl;
}
}
}
34 56 * 11 2 / +
1 2 3 * + 4 -u 2 * +
33 55 2 * +
1909
-1
143
-
- )을 newQueue의 capacity-front-1에서부터 복사해 넣는다
template <class T> void Queue<T>::Push(const& x) {// add x at rear of queue. if ((rear + 1) % capacity == front) { // queue capacity 2배 확장 코드 T* newQueue = new T[2*capacity]; // copy from queue to new Queue int start = (front+1) % capacity; if (start < 2) // no wrap around copy(queue+start, queue+start+capacity-1, newQueue); else { copy(queue+start, queue+capacity, newQueue); copy(queue, queue+rear+1, newQueue+capacity-start); } // switch to newQueue front = 2*capacity – 1; rear = capacity-2; capacity *= 2; delete [] queue; queue = newQueue; } rear = (rear+1)%capacity; queue[rear] =x; }
- pop 구현
- template <class T> void Queue<T>::Pop() {// 큐로부터 원소를 삭제한다. if(isEmpty()) throw “Queue is empty. Cannot delete.”; front = (front+1)%capacity; queue[front].~T(); // T를 위한 파괴자 }
미로 maze
- 1≤i≤m이고 1≤j≤p인 이차원 배열 maze[m][p]로 표현
- 1 : 통로가 막혀 있음, 0 : 통과할 수 있음
- 경계선에 있는 경우 : i ==1, i == m , j==1, j == p 인 경우
- 경계 조건을 매번 검사하지 않기 위해 미로의 주위를 1로 둘러쌈
- 배열은 maze[m+2][p+2]로 선언
- 이동 방향 미리 정의하는 방법
- 구현
maze.cpp
#include <iostream>
#include <stack>
using namespace std;
const int MAXSIZE = 100;
bool maze[MAXSIZE + 2][MAXSIZE + 2];
bool mark[MAXSIZE + 1][MAXSIZE + 1] = { 0 };
enum directions { N, NE, E, SE, S, SW, W, NW };
struct offsets {
int a, b;
}
movea[8] = { -1,0, -1,1, 0,1, 1,1, 1,0, 1,-1, 0,-1, -1,-1 };
struct Items {
Items(int xx = 0, int yy = 0, int dd = 0) : x(xx), y(yy), dir(dd) {}
int x, y, dir; // dir은 다음에 방문 예정인 방향
};
template <class T>
ostream& operator<< (ostream& os, stack<T>& s) {
// 스택의 내용을 역순으로 출력
stack<T> s2;
// 내용을 하나씩 꺼내 다른 임시 스택에 넣어 저장한 후,
while (!s.empty()) { s2.push(s.top()); s.pop(); }
// 그 임시 스택에서 하나씩 꺼내 출력
while (!s2.empty()) {
os << " -> " << s2.top();
s.push(s2.top()); s2.pop();
}
return os;
}
ostream& operator<<(ostream& os, Items& item) {
// 5개의 Items가 출력될 때마다 줄 바꾸기 위한 변수
static int count = 0;
os << "(" << item.x << "," << item.y << ")";
count++;
if ((count % 5) == 0) cout << endl;
return os;
}
void Path(const int m, const int p) {
mark[1][1] = 1;
// C++ STD stack을 이용하기
stack<Items> stack;
// (1,1)에서 E방향부터(시계방향) 순차적으로 시도함
Items temp(1, 1, E);
stack.push(temp);
while (!stack.empty()) {
temp = stack.top(); stack.pop();
// (i,j)부터 (g,h)로 이동한다
int i = temp.x; int j = temp.y; int d = temp.dir;
while (d < 8) {
int g = i + movea[d].a; int h = j + movea[d].b;
if ((g == m) && (h == p)) { // 출구에 도착하면
// 경로를 출력한다.
cout << stack;
// 경로 상의 마지막 두 위치
temp.x = i; temp.y = j; cout << "->" << temp;
temp.x = m; temp.y = p; cout << "->" << temp << endl;
return;
}
// 방문하지 않은 새로운 위치라면
if ((!maze[g][h]) && (!mark[g][h])) {
// 방문했다고 표시한다.
mark[g][h] = 1;
// 현 위치와 실패 시 다음에 시도할 방향을 저장한다.
temp.x = i; temp.y = j; temp.dir = d + 1;
stack.push(temp);
// (g, h)로 이동한다. N방향부터 시계방향으로 시도하자.
i = g; j = h; d = N;
}
else d++;
}
}
cout << "No path in maze." << endl;
}
void getdata(istream& is, int& m, int& p) {
// 자료화일을 읽어들여 maze에 저장한다
is >> m >> p;
for (int i = 0; i < m + 2; i++) { maze[i][0] = 1; maze[i][p + 1] = 1; }
for (int j = 1; j <= p; j++) { maze[0][j] = 1; maze[m + 1][j] = 1; }
for (int i = 1; i <= m; i++)
for (int j = 1; j <= p; j++)
is >> maze[i][j];
}
#include <iostream>
#include <fstream>
using namespace std;
void getdata(istream&, int&, int&);
void Path(int, int);
int main(int argc, char* argv[]) {
int m, p; // m by p maze
if (argc == 1)
cerr << "Usage: " << argv[0] << " maze_data_file" << endl;
else {
ifstream is(argv[1]);
if (!is) { cerr << argv[1] << " does not exist\\n"; exit(1); }
cout << "For maze datafile (" << argv[1] << ")\\n";
getdata(is, m, p); is.close();
Path(m, p);
}
}
12 15
0 1 0 0 0 1 1 0 0 0 1 1 1 1 1
1 0 0 0 1 1 0 1 1 1 0 0 1 1 1
0 1 1 0 0 0 0 1 1 1 1 0 0 1 1
1 1 0 1 1 1 1 0 1 1 0 1 1 0 0
1 1 0 1 0 0 1 0 1 1 1 1 1 1 1
0 0 1 1 0 1 1 1 0 1 0 0 1 0 1
0 0 1 1 0 1 1 1 0 1 0 0 1 0 1
0 1 1 1 1 0 0 1 1 1 1 1 1 1 1
0 0 1 1 0 1 1 0 1 1 1 1 1 0 1
1 1 0 0 0 1 1 0 1 1 0 0 0 0 0
0 0 1 1 1 1 1 0 0 0 1 1 1 1 0
0 1 0 0 1 1 1 1 1 0 1 1 1 1 0
For maze datafile (mazein.txt)
-> (1,1) -> (2,2) -> (1,3) -> (1,4) -> (1,5)
-> (2,4) -> (3,5) -> (3,4) -> (4,3) -> (5,3)
-> (6,2) -> (7,2) -> (8,1) -> (9,2) -> (10,3)
-> (10,4) -> (9,5) -> (8,6) -> (8,7) -> (9,8)
-> (10,8) -> (11,9) -> (11,10) -> (10,11) -> (10,12)
-> (10,13) -> (9,14) -> (10,15)->(11,15)->(12,15)
후위 표기식
- 장점
- 괄호가 불필요
- 연산자의 우선 순위가 무의미
- 계산 과정이 간단 (왼편에서 오른편으로 스캔)
- 중위 표기식 → 후위 표기식 변환 알고리즘
- 식을 전부 괄호로 묶는다
- 이항 연산자들을 모두 자기 오른쪽 괄호로 이동시킨다.
- 괄호를 전부 삭제한다.
- 연산자 우선 순위
- 우선순위를 기반으로 스택에 push와 pop을 수행
- ‘(‘ 의 우선순위가 문제임
- 스택에 들어가기 전에는 가장 높은 우선순위의 연산자
- 스택 속에 있을 때는 ‘)’가 나타날 때까지 낮은 우선순위의 연산자
- so, 스택 top의 연산자의 isp가 새로운 연산자의 icp보다 산술적으로 작거나같을 때 스택에서 pop되어 출력 for (; isp(stack.Top()) <= icp(x); stack.Pop())
- 예시
- 구현
- Expression e에 대한 NextToken(e)에서 unary – 와 binary –를 어떻게 구별하는가?
- Infix Notation으로 된 Expression인 경우 Operand(피연산자) 후에 나온 –는 binary –이고, Operator(연산자) 후에 나온 –는 unary –이다. 단 맨 처음에 만난 –는 unary-로 간주한다.
- Postfix Notation으로 된 Expression인 경우 입력자체가 binary –(-)와 unary –(-u)로 구별해야 함.
- Expression e에 대한 NextToken(e)에서 unary – 와 binary –를 어떻게 구별하는가?
post.cpp
#include <iostream>
#include <stack>
#include "post.h"
using namespace std;
bool Token::operator==(char b) { return len == 1 && str[0] == b; }
bool Token::operator!=(char b) { return len != 1 || str[0] != b; }
Token::Token() {}
// 1-char token: type equals the token itself
Token::Token(char c) : len(1), type(c) {
str = new char[1]; str[0] = c;
} // default type = c itself
// 2-char token(e.g. <=) & its type(e.g. LE)
Token::Token(char c, char c2, int ty) : len(2), type(ty) {
str = new char[2]; str[0] = c; str[1] = c2;
}
//operand with its length & type(defaulted to ID)
Token::Token(char* arr, int l, int ty = ID) : len(l), type(ty) {
str = new char[len + 1];
for (int i = 0; i < len; i++) str[i] = arr[i];
str[len] = '\\0';
if (type == NUM) {
ival = arr[0] - '0';
for (int i = 1; i < len; i++) ival = ival * 10 + arr[i] - '0';
}
else if (type == ID) ival = 0;
else throw "must be ID or NUM";
}
// 피연산자(ID 또는 NUM)인지 확인
bool Token::IsOperand() {
return type == ID || type == NUM;
}
ostream& operator<<(ostream& os, Token t) {
if (t.type == UMINUS) os << "-u";
else if (t.type == NUM) os << t.ival;
else for (int i = 0; i < t.len; i++) os << t.str[i];
os << " ";
return os;
}
bool GetID(Expression& e, Token& tok) {
char arr[MAXLEN]; // ID 저장할 배열
int idlen = 0;
char c = e.str[e.pos];
if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) return false;
arr[idlen++] = c;
e.pos++;
while ((c = e.str[e.pos]) >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') {
arr[idlen++] = c; e.pos++;
}
arr[idlen] = '\\0';
tok = Token(arr, idlen, ID);
return true;
}
bool GetInt(Expression& e, Token& tok) {
char arr[MAXLEN];
int idlen = 0;
char c = e.str[e.pos];
if (!(c >= '0' && c <= '9')) return false;
arr[idlen++] = c;
e.pos++;
while ((c = e.str[e.pos]) >= '0' && c <= '9') {
arr[idlen++] = c; e.pos++;
}
arr[idlen] = '\\0';
tok = Token(arr, idlen, NUM);
return true;
}
void SkipBlanks(Expression& e) {
char c;
while (e.pos < e.len && ((c = e.str[e.pos]) == ' ' || c == '\\t'))
e.pos++;
}
// 두 글자 토큰들 처리: <= >= == != && || -u
bool TwoCharOp(Expression& e, Token& tok) {
char c = e.str[e.pos]; char c2 = e.str[e.pos + 1];
int op; // LE GE EQ NE AND OR UMINUS
if (c == '<' && c2 == '=') op = LE;
else if (c == '>' && c2 == '=') op = GE;
else if (c == '=' && c2 == '=') op = EQ;
else if (c == '!' && c2 == '=') op = NE;
else if (c == '&' && c2 == '&') op = AND;
else if (c == '|' && c2 == '|') op = OR;
else if (c == '-' && c2 == 'u') op = UMINUS;
else
return false;
tok = Token(c, c2, op); e.pos += 2;
return true;
}
// 한 글자 토큰들 처리
bool OneCharOp(Expression& e, Token& tok) {
char c = e.str[e.pos];
if (c == '-' || c == '!' || c == '*' || c == '/' || c == '%' ||
c == '+' || c == '<' || c == '>' || c == '(' || c == ')' || c == '=') {
tok = Token(c); e.pos++;
return true;
}
return false;
}
// 2nd arg=true for infix expression
Token NextToken(Expression& e, bool INFIX = true) {
// 종전에 연산자 발견되었다고 가정
static bool oprrFound = true;
Token tok;
// 빈칸이 있다면 무시
SkipBlanks(e);
// 더 이상 남은 토큰이 없는 경우
if (e.pos == e.len)
{ if (INFIX) oprrFound = true; return Token('#'); }
if (GetID(e, tok) || GetInt(e, tok))
{ if (INFIX) oprrFound = false; return tok; }
if (TwoCharOp(e, tok) || OneCharOp(e, tok)) {
// 연산자 다음에 -가 나온 경우 -u로 바꿔라
if (tok.type == '-' && INFIX && oprrFound)
tok = Token('-', 'u', UMINUS);
if (INFIX) oprrFound = true; return tok;
}
throw "Illegal Character Found";
}
// in-coming priority
int icp(Token& t) {
int ty = t.type;
int ty_num = -1;
// ty가 '('면, 0, UMiNUS나 '!'면 1,'*'나 '/'나 '%'aus 2, '+'나 '-'면 3, '<'나 '>'나 LE나 GE면 4,
// EQ나 NE면 5, AND면 6, OR이면 7, '='이면 8,'#'면 9를 return 한다.
if (ty == '(') ty_num = 0;
else if (ty == UMINUS || ty == '!') ty_num = 1;
else if (ty == '*' || ty == '/' || ty == '%') ty_num = 2;
else if (ty == '+' || ty == '-') ty_num = 3;
else if (ty == '<' || ty == '>' || ty == LE || ty == GE) ty_num = 4;
else if (ty == EQ || ty == NE) ty_num = 5;
else if (ty == AND) ty_num = 6;
else if (ty == OR) ty_num = 7;
else if (ty == '=') ty_num = 8;
else if (ty == '#') ty_num = 9;
else ty_num = -1;
return ty_num;
}
//in-stack priority
int isp(Token& t) {
int ty = t.type;
int ty_num = -1;
// icp에서 (랑 #만 바뀜
if (ty == '(') ty_num = 9;
else if (ty == UMINUS || ty == '!') ty_num = 1;
else if (ty == '*' || ty == '/' || ty == '%') ty_num = 2;
else if (ty == '+' || ty == '-') ty_num = 3;
else if (ty == '<' || ty == '>' || ty == LE || ty == GE) ty_num = 4;
else if (ty == EQ || ty == NE) ty_num = 5;
else if (ty == AND) ty_num = 6;
else if (ty == OR) ty_num = 7;
else if (ty == '=') ty_num = 8;
else if (ty == '#') ty_num = 10;
else ty_num = -1;
return ty_num;
}
// 중위 표기식 e의 후위 표기식을 출력한다.
void Postfix(Expression e) {
// 스택 초기화
stack<Token> stack;
// End Of String 표시
stack.push('#');
for (Token x = NextToken(e); x != '#'; x = NextToken(e))
if (x.IsOperand()) cout << x;
else if (x == ')') {
// '('까지 스택에서 꺼낸다.
for (; stack.top() != '('; stack.pop())
cout << stack.top();
// 'C'를 제거한다.
stack.pop();
} else { // x가 연산자일 떄
for (; isp(stack.top()) <= icp(x); stack.pop()) {
// 예외처리: a = b = 0 같은 우결합 연산자에선
// 나중에 나온 = 이 우선순위가 높음.
if (x == '=' && stack.top() == '=') break;
cout << stack.top();
}
stack.push(x);
}
// 마지막 '#'의 출력은 생략한다.
while (stack.top() != '#') { cout << stack.top(); stack.pop(); }
stack.pop();
cout << endl;
}
post.h
#ifndef POST_H
#define POST_H
// token types for non one-char tokens
#define ID 257
#define NUM 258
#define EQ 259
#define NE 260
#define GE 261
#define LE 262
#define AND 263
#define OR 264
#define UMINUS 265
#define MAXLEN 80
struct Expression {
Expression(char* s) : str(s), pos(0) { for (len = 0; str[len] != '\\0'; len++); }
char* str;
int pos;
int len;
};
struct Token {
bool operator==(char);
bool operator!=(char);
Token();
Token(char); // 1-char token: type equals the token itself
Token(char, char, int); // 2-char token(e.g. <=) & its type(e.g. LE)
Token(char*, int, int); //operand with its length & type(defaulted to ID)
bool IsOperand(); // true if the token type is ID or NUM
int type; // ascii code for 1-char op; predefined for other tokens
char* str; // token value
int len; // length of str
int ival; // used to store an integer for type NUM; init to 0 for ID
};
using namespace std;
ostream& operator<<(ostream& os, Token t);
Token NextToken(Expression&, bool); // 2nd arg=true for infix expression
#endif
A * B * C
-A + B - C + D
A * -B + C
(A + B) * D + E / (F + A * D) + C
A B * C *
A -u B + C - D +
A B -u * C +
A B + D * E F A D * + / + C +
A * B * C
-A + B - C + D
A * -B + C
(A + B) * D + E / (F + A * D) + C
A B * C *
A -u B + C - D +
A B -u * C +
A B + D * E F A D * + / + C +
A && B||C||!(E>F)
!(A&&!((B<C)||(C>D)))||(C<E)
A B && C || E F > ! ||
A B C < C D > || ! && ! C E < ||
34 * 56 + 11 / 2
1 + 2 * 3 + - 4 * 2s
34 56 * 11 2 / +
1 2 3 * + 4 -u 2 s * +
33 + 55 * 2
an77 = 2 + 7 * 5
b = 2
an77 + b * 2
a + 5
33 55 2 * +
an77 2 7 5 * + =
b 2 =
an77 b 2 * +
a 5 +
eval.cpp
#include <iostream>
#include <stack>
#include "post.h"
using namespace std;
int GetVal(Token& opnd) {
if (opnd.type == NUM) return opnd.ival;
return 0; // ID인 경우는 0을 return
}
// UMINUS와 ‘!’의 두가지 경우만 처리함
Token UnaryOp(int op, Token& x) {
if (!x.IsOperand()) throw "Operand Only for Unary Operator";
Token tmp; tmp.type = NUM;
if (op == UMINUS) tmp.ival = -GetVal(x);
else if (op == '!') tmp.ival = !GetVal(x);
else throw "Invalid unary operator";
return tmp;
}
Token BinaryOp(int op, Token& left, Token& right) {
if (!left.IsOperand() || !right.IsOperand())
throw "Two Operands required for Binary Operation";
Token tmp; tmp.type = NUM;
int val1 = GetVal(left), val2 = GetVal(right);
if (op == '+') tmp.ival = val1 + val2;
else if (op == '-') tmp.ival = val1 - val2;
else if (op == '*') tmp.ival = val1 * val2;
else if (op == '/') tmp.ival = val1 / val2;
else if (op == '%') tmp.ival = val1 % val2;
else if (op == '<') tmp.ival = val1 < val2;
else if (op == '>') tmp.ival = val1 > val2;
else if (op == GE) tmp.ival = val1 >= val2;
else if (op == LE) tmp.ival = val1 <= val2;
else if (op == EQ) tmp.ival = val1 == val2;
else if (op == NE) tmp.ival = val1 != val2;
else if (op == NE) tmp.ival = val1 && val2;
else if (op == NE) tmp.ival = val1 || val2;
else if (op == '=') throw "Assignment Not Yet Implemented";
else throw "No such binary operator";
return tmp;
}
// 스택의 top에 있는 것을 꺼내어 return한다.
Token DeleteFromStack(stack<Token>& stack) {
if (stack.empty()) throw "Trying to pop from an empty stack";
Token tmp = stack.top();
stack.pop();
return tmp;
}
// postfix 표현식을 입력으로 받아 그 값을 계산한다.
void Eval(Expression e) {
stack<Token> stack;
Token opnd1, opnd2;
for (Token x = NextToken(e, false); x != '#'; x = NextToken(e, false))
if (x.IsOperand()) stack.push(x);
else { // x is an operator
// unary operator
if (x.type == UMINUS || x.type == '!') {
opnd1 = DeleteFromStack(stack);
stack.push(UnaryOp(x.type, opnd1));
}
else { // binary operator
opnd1 = DeleteFromStack(stack);
opnd2 = DeleteFromStack(stack);
stack.push(BinaryOp(x.type, opnd2, opnd1));
}
}
// 최종 계산 결과 출력
cout << DeleteFromStack(stack) << endl;
}
posteval.cpp
#include <iostream>
#include "post.h"
using namespace std;
void Eval(Expression);
int main(void)
{
char line[MAXLEN];
while (cin.getline(line, MAXLEN)) {
Expression e(line); // Assume postfix notation
try {
Eval(e);
} catch (char const* msg) {
cerr << "Exception: " << msg << endl;
}
}
}
34 56 * 11 2 / +
1 2 3 * + 4 -u 2 * +
33 55 2 * +
1909
-1
143
'[전공] > [자료구조]' 카테고리의 다른 글
[C++ 자료구조론] 2장 배열 (1) | 2022.10.30 |
---|---|
[C++ 자료구조론] 1장 기본 개념 (1) | 2022.10.30 |