STL을 활용한 데이터 처리 프로그램
실습 개요
이 실습에서는 STL(Standard Template Library)의 다양한 컨테이너, 알고리즘, 그리고 함수 객체를 활용하여 실제 데이터 처리 프로그램을 구현해볼 것입니다.
이를 통해 STL의 실제 응용 방법과 효율적인 데이터 처리 기법을 학습할 수 있습니다.
학습 목표
- STL 컨테이너를 적절히 선택하여 데이터 저장 및 관리하는 방법 학습
- STL 알고리즘을 활용한 데이터 처리 및 분석 기법 이해
- 함수 객체와 람다 표현식을 이용한 커스텀 연산 구현 능력 향상
- 실제 문제 해결을 위한 STL의 종합적 활용 능력 개발
프로그램 요구사항
도서관 관리 시스템을 구현하되, 다음 기능을 포함해야 합니다.
- 도서 정보 (제목, ISBN, 저자, 출판년도) 입력 및 저장
- 전체 도서 목록 출력
- 다양한 기준(제목, 저자, 출판년도)으로 도서 정렬
- 특정 조건(저자, 출판년도 범위)에 맞는 도서 검색
- 가장 최근에 출판된 N권의 도서 목록 출력
- 도서 정보 수정 및 삭제
- 도서관 통계 정보 계산 (총 도서 수, 특정 연도 이후 출판된 도서 비율 등)
프로그램 구현
필요한 헤더 파일과 네임스페이스 선언
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <functional>
#include <iomanip>
#include <numeric>
#include <map>
using namespace std;
도서 클래스 정의
class Book {
public:
string title;
string isbn;
string author;
int publicationYear;
Book(string t, string i, string a, int y)
: title(t), isbn(i), author(a), publicationYear(y) {}
// 출력을 위한 연산자 오버로딩
friend ostream& operator<<(ostream& os, const Book& b) {
os << setw(30) << b.title << setw(15) << b.isbn
<< setw(20) << b.author << setw(6) << b.publicationYear;
return os;
}
};
도서관 관리 클래스 정의
class LibraryManagementSystem {
private:
vector<Book> books;
public:
void addBook(const Book& book) {
books.push_back(book);
}
void printAllBooks() const {
for (const auto& book : books) {
cout << book << endl;
}
}
void sortBooks(function<bool(const Book&, const Book&)> comp) {
sort(books.begin(), books.end(), comp);
}
vector<Book> findBooks(function<bool(const Book&)> predicate) const {
vector<Book> result;
copy_if(books.begin(), books.end(), back_inserter(result), predicate);
return result;
}
vector<Book> getRecentBooks(int n) const {
vector<Book> recentBooks = books;
partial_sort(recentBooks.begin(), recentBooks.begin() + min(n, (int)recentBooks.size()),
recentBooks.end(),
[](const Book& a, const Book& b) {
return a.publicationYear > b.publicationYear;
});
recentBooks.resize(min(n, (int)recentBooks.size()));
return recentBooks;
}
void updateBook(const string& isbn, function<void(Book&)> updateFunc) {
auto it = find_if(books.begin(), books.end(),
[&isbn](const Book& b) { return b.isbn == isbn; });
if (it != books.end()) {
updateFunc(*it);
}
}
void removeBook(const string& isbn) {
books.erase(
remove_if(books.begin(), books.end(),
[&isbn](const Book& b) { return b.isbn == isbn; }),
books.end()
);
}
map<string, int> getAuthorBookCount() const {
map<string, int> authorCount;
for (const auto& book : books) {
authorCount[book.author]++;
}
return authorCount;
}
double getRecentBookRatio(int year) const {
int recentBooks = count_if(books.begin(), books.end(),
[year](const Book& b) { return b.publicationYear >= year; });
return static_cast<double>(recentBooks) / books.size();
}
};
메인 함수 및 사용자 인터페이스
int main() {
LibraryManagementSystem lms;
// 샘플 데이터 추가
lms.addBook(Book("The Great Gatsby", "9780743273565", "F. Scott Fitzgerald", 1925));
lms.addBook(Book("To Kill a Mockingbird", "9780446310789", "Harper Lee", 1960));
lms.addBook(Book("1984", "9780451524935", "George Orwell", 1949));
lms.addBook(Book("Pride and Prejudice", "9780141439518", "Jane Austen", 1813));
lms.addBook(Book("The Catcher in the Rye", "9780316769174", "J.D. Salinger", 1951));
int choice;
do {
cout << "\n1. 도서 추가" << endl;
cout << "2. 전체 도서 목록 출력" << endl;
cout << "3. 도서 정렬" << endl;
cout << "4. 도서 검색" << endl;
cout << "5. 최근 출판된 도서 목록" << endl;
cout << "6. 도서 정보 수정" << endl;
cout << "7. 도서 삭제" << endl;
cout << "8. 저자별 도서 수 통계" << endl;
cout << "9. 최근 출판 도서 비율 계산" << endl;
cout << "0. 종료" << endl;
cout << "선택: ";
cin >> choice;
switch(choice) {
case 1: {
string title, isbn, author;
int year;
cout << "제목, ISBN, 저자, 출판년도 입력: ";
cin >> title >> isbn >> author >> year;
lms.addBook(Book(title, isbn, author, year));
break;
}
case 2:
lms.printAllBooks();
break;
case 3: {
cout << "정렬 기준 (1: 제목, 2: 저자, 3: 출판년도): ";
int sortChoice;
cin >> sortChoice;
switch(sortChoice) {
case 1:
lms.sortBooks([](const Book& a, const Book& b) { return a.title < b.title; });
break;
case 2:
lms.sortBooks([](const Book& a, const Book& b) { return a.author < b.author; });
break;
case 3:
lms.sortBooks([](const Book& a, const Book& b) { return a.publicationYear < b.publicationYear; });
break;
}
lms.printAllBooks();
break;
}
case 4: {
cout << "검색 기준 (1: 저자, 2: 출판년도 범위): ";
int searchChoice;
cin >> searchChoice;
vector<Book> result;
if (searchChoice == 1) {
string author;
cout << "저자 이름: ";
cin >> author;
result = lms.findBooks([&author](const Book& b) { return b.author == author; });
} else if (searchChoice == 2) {
int startYear, endYear;
cout << "시작 연도와 끝 연도: ";
cin >> startYear >> endYear;
result = lms.findBooks([startYear, endYear](const Book& b) {
return b.publicationYear >= startYear && b.publicationYear <= endYear;
});
}
for (const auto& book : result) {
cout << book << endl;
}
break;
}
case 5: {
int n;
cout << "출력할 도서 수: ";
cin >> n;
auto recentBooks = lms.getRecentBooks(n);
for (const auto& book : recentBooks) {
cout << book << endl;
}
break;
}
case 6: {
string isbn;
cout << "수정할 도서의 ISBN: ";
cin >> isbn;
string newTitle;
cout << "새 제목: ";
cin >> newTitle;
lms.updateBook(isbn, [&newTitle](Book& b) { b.title = newTitle; });
break;
}
case 7: {
string isbn;
cout << "삭제할 도서의 ISBN: ";
cin >> isbn;
lms.removeBook(isbn);
break;
}
case 8: {
auto authorStats = lms.getAuthorBookCount();
for (const auto& [author, count] : authorStats) {
cout << author << ": " << count << " books" << endl;
}
break;
}
case 9: {
int year;
cout << "기준 연도: ";
cin >> year;
double ratio = lms.getRecentBookRatio(year);
cout << year << "년 이후 출판된 도서 비율: " << fixed << setprecision(2) << ratio * 100 << "%" << endl;
break;
}
case 0:
cout << "프로그램을 종료합니다." << endl;
break;
default:
cout << "잘못된 선택입니다." << endl;
}
} while (choice != 0);
return 0;
}
코드 설명
vector<Book>
을 사용하여 도서 정보를 저장합니다.sort
,copy_if
,partial_sort
등의 STL 알고리즘을 활용하여 다양한 데이터 처리 작업을 수행합니다.- 람다 표현식을 사용하여 커스텀 비교 함수와 조건을 구현합니다.
find_if
,remove_if
등을 사용하여 특정 조건을 만족하는 요소를 찾거나 제거합니다.function
객체를 활용하여 유연한 함수 인자 전달을 구현합니다.
주요 STL 사용 예시 설명
1. sort
알고리즘 사용
sort(books.begin(), books.end(), comp);
이 코드는 comp
함수 객체를 사용하여 books
벡터를 정렬합니다.
2. copy_if
알고리즘 사용
copy_if(books.begin(), books.end(), back_inserter(result), predicate);
이 코드는 predicate
조건을 만족하는 요소만 result
벡터에 복사합니다.
3. partial_sort
알고리즘 사용
partial_sort(recentBooks.begin(), recentBooks.begin() + n, recentBooks.end(),
[](const Book& a, const Book& b) {
return a.publicationYear > b.publicationYear;
});
이 코드는 출판년도를 기준으로 상위 n개의 도서만 정렬합니다.
4. find_if
알고리즘 사용
auto it = find_if(books.begin(), books.end(),
[&isbn](const Book& b) { return b.isbn == isbn; });
이 코드는 특정 ISBN을 가진 도서를 찾습니다.
5. count_if
알고리즘 사용
int recentBooks = count_if(books.begin(), books.end(),
[year](const Book& b) { return b.publicationYear >= year; });
이 코드는 특정 연도 이후에 출판된 도서의 수를 계산합니다.
추가 기능 구현 연습
1. 도서 대출 및 반납 기능 구현
Book
클래스에bool isAvailable
멤버 변수 추가- 대출 및 반납 함수 구현
- 현재 대출 중인 도서 목록 출력 기능 추가
2. 도서 평점 시스템 구현
Book
클래스에vector<double> ratings
멤버 변수 추가- 평점 추가 및 평균 평점 계산 함수 구현
- 평점 기준 도서 정렬 기능 추가
3. 도서 카테고리 시스템 구현
Book
클래스에string category
멤버 변수 추가- 카테고리별 도서 검색 및 통계 기능 구현
성능 최적화 고려사항
1. 대용량 데이터 처리
- 도서 수가 매우 많을 경우, 인덱싱이나 해시 테이블 사용 고려
- 정렬된 상태를 유지하며 삽입하는 방법 고려 (
std::set
또는std::map
사용)
2. 검색 성능 향상
- 자주 사용되는 검색 키에 대해 인덱스 구현 고려
- 이진 검색 활용 (정렬된 데이터의 경우)
3. 메모리 사용 최적화
- 필요한 경우
std::vector
의reserve
함수를 사용하여 불필요한 재할당 방지 - 큰 객체의 경우 복사 대신 참조 사용 고려
예외 처리
프로그램의 안정성을 높이기 위해 다음과 같은 예외 처리를 추가할 수 있습니다.
try {
// 사용자 입력 또는 파일 입출력 등의 작업
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid input: " << e.what() << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Runtime error: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Unknown error occurred" << std::endl;
}
파일 입출력 기능 추가
도서 정보를 파일로 저장하고 불러오는 기능을 구현해 봅시다.
#include <fstream>
class LibraryManagementSystem {
// ... 기존 코드 ...
public:
void saveToFile(const string& filename) const {
ofstream file(filename);
if (file.is_open()) {
for (const auto& book : books) {
file << book.title << "," << book.isbn << ","
<< book.author << "," << book.publicationYear << "\n";
}
}
}
void loadFromFile(const string& filename) {
ifstream file(filename);
string line;
while (getline(file, line)) {
stringstream ss(line);
string title, isbn, author;
int year;
getline(ss, title, ',');
getline(ss, isbn, ',');
getline(ss, author, ',');
ss >> year;
addBook(Book(title, isbn, author, year));
}
}
};
연습 문제
- 도서 제목의 부분 문자열로 검색하는 기능을 구현하세요. (힌트 :
std::string::find
사용) - 특정 연도 범위 내에서 가장 많은 책을 출판한 저자를 찾는 기능을 구현하세요.
- 도서 대여 시스템을 구현하세요. 대여 가능한 도서 목록, 대여 중인 도서 목록, 반납 예정일 등의 정보를 관리해야 합니다.
결론
이 실습을 통해 STL의 다양한 컨테이너와 알고리즘을 실제 문제 해결에 적용해보았습니다. 도서관 관리 시스템이라는 실제적인 예제를 통해 데이터 저장, 검색, 정렬, 필터링 등의 작업을 STL을 활용하여 효율적으로 구현할 수 있음을 확인했습니다. STL을 효과적으로 사용하면 코드의 가독성, 유지보수성, 효율성을 크게 향상시킬 수 있습니다. 앞으로 더 복잡한 프로그램을 개발할 때도 이러한 STL의 기능을 적극 활용하시기 바랍니다. 실제 프로젝트에서는 이 예제를 바탕으로 더 복잡한 기능(예 : 사용자 인증, 데이터베이스 연동, 네트워크 기능 등)을 추가하여 완전한 도서관 관리 시스템을 구축할 수 있을 것입니다.