Chater 3-2. 추상화된 리스트로 구체화한 영화 평점 리스트 (연결리스트 사용)
카테고리: DataStructure2
태그: C Data Structure Algorithm
홍정모 교수님의 강의 홍정모의 따라하며 배우는 C언어(부록) 를 듣고 정리한 필기입니다. 😀
ppt 캡처는 모두 교수님 강의에서 캡처한 것임을 밝힙니다.
Chapter 3. 추상화
🚀 인터페이스의 필요성
“어떤 데이터 아이템을 담을 수 있는 리스트”로서 추상화 한다. 👉 일반화!! 영화 평점 뿐만 아니라 어떤 데이터든 담을 수 있는 리스트로서 일반화하기. 모든 아이템 리스트들이 가질 수 있는 공통된 "객체"와 "연산"을 정리하여 일반화할 수 있겠다.
- 1️⃣ “객체” 일반화 (OOP 개념에서 보자면 멤버 변수)
- 2️⃣ “연산” 일반화 (OOP 개념에서 보자면 추상 함수)
🚀 추상화 과정
- 1️⃣ 데이터와 자료 구조의 분리
- 영화 데이터만 담을 수 있는 리스트에서 모든 종류의 데이터를 담을 수 있는 리스트로서 일반화 해야 하기 떄문
- 환자 리스트, 영화 리스트, 쇼핑 리스트 같은 다양한 데이터를 담는 리스트로 구체화 될 수 있도록 “리스트” 라는 추상적인 개념을 일반화하는 것이기 때문에 데이터와 분리해야한다.
- 이렇게 분리하면 구체화할 데이터를 다른 것으로 바꿀 때 쉽게 수정이 가능하다.
- 영화 평점 리스트에서 쇼핑 리스트로 쉽게 바꿀 수 있음!
- 2️⃣ 코드 재사용
- 왼쪽 코드가 오른쪽 코드로 간단해질 수 있다.
🚀 영화 평점 리스트 일반화하기 (어떤 리스트로든 구체화 될 수 있도록!)
“리스트”자체를 일반화 해놓으면 나중에 영화 리스트를 환자 리스트로 바꾸더라도 Data 타입만 ‘환자’ 필드로 바꾸면 그만이다!
📜SimpleList.h
#pragma once
#include <stdbool.h>
#include <stdio.h>
#define TSIZE 45
// 나중에 영화 리스트 말고 다른 데이터 리스트 쓸거라면
// 여기만 수정하면 땡!!!!! 💛✨💛✨💛✨
// 어떤 데이터를 담을 리스트냐에 따라
typedef struct movie {
char name[TSIZE];
int year;
// struct movie* next;
}Item;
typedef struct node {
Item item;
struct node* next;
}Node;
// 연결리스트 자료구조
typedef struct list {
Node* head; // head pointer
int size; // number of items
}List;
void InitializeList(List* plist);
bool IsEmpty(const List* plist);
bool IsFull(const List* plist);
bool AddItem(Item item, List* plist);
void InsertbyIndex(Item item, List* plist, int index);
void RemoveByIndex(List* plist, int index);
bool InsertItemFront(Item item, List* plist);
bool Find(const List* plist, Item item_to_find, int* index, Item* item_found, bool (*compare_func)(Item a, Item b));
unsigned int CountItems(const List* plist);
unsigned int PrintAllItems(const List* plist, void (*print_an_item_func)(Item item));
unsigned int WriteAllItems(const List* plist, FILE* file, void (*write_an_item_func)(FILE* file, Item item));
void Traverse(const List* plist, void (*pfun)(Item item));
void ClearList(List* plist);
bool FindItemByIndex(const List* plist, const int index, Item** item);
Node* FindNodeByIndex(const List* plist, const int index);
void RemoveFirstItem(List* plist);
void RemoveNextItem(Node* prev);
unsigned int ReadFromFile(List* plist, const char* filename, bool (*read_an_item_func)(FILE* file, Item* item));
unsigned int FindAndRun(const List* plist, Item item_to_find, bool (*compare_func)(Item a, Item b), void (*func_run)(Item item));
📜SimpleList.c
#include "SimpleList.h"
#include <stdbool.h>
#include <stdio.h>
static void CopyToNode(Item item, Node* pnode)
{
pnode->item = item;
}
void InitializeList(List* plist)
{
plist->head = NULL;
}
bool IsEmpty(const List* plist)
{
if (plist->head == NULL)
return true;
else
return false;
}
// 연결리스트라면 꽉 찰 일이 거의 발생하지 않겠지만 배열일 경우엔 중요하게 작용할 함수
bool IsFull(const List* plist)
{
Node* pt;
bool full;
pt = (Node*)malloc(sizeof(Node));
if (pt == NULL) // 실패했다면 꽉차서 더 이상 만들 수 없는걸로
full = true;
else
full = false;
free(pt);
return full;
}
unsigned int CountItems(const List* plist)
{
unsigned int count = 0;
Node* pnode = plist->head;
while (pnode != NULL) {
++count;
pnode = pnode->next;
}
return count;
}
bool InsertItemFront(Item item, List* plist)
{
Node* new_node;
new_node = (Node*)malloc(sizeof(Node));
if (new_node == NULL) {
priintf("Malloc failed.\n");
return false;
}
CopyToNode(item, new_node);
Node* temp = plist->head;
plist->head = new_node;
new_node->next = temp;
return true;
}
bool AppendItem(Item item, Node* prev) // prev 뒤에 아이템 추가
{
Node* new_node;
new_node = (Node*)malloc(sizeof(Node));
if (new_node == NULL) {
priintf("Malloc failed.\n");
return false;
}
CopyToNode(item, new_node);
Node* temp = prev->next;
prev->next = new_node;
new_node->next = temp;
return true;
}
bool AddItem(Item item, List* plist) // 맨 뒤에 추가
{
Node* new_node;
Node* search = plist->head;
new_node = (Node*)malloc(sizeof(Node));
if (new_node == NULL) {
priintf("Malloc failed.\n");
return false;
}
CopyToNode(item, new_node);
new_node->next = NULL;
if (search == NULL)
plist->head = new_node;
else {
while (search->next != NULL)
search = search->next;
search->next = new_node;
}
return true;
}
// 구체화 될 아이템에 따라 출력해야 할게 다르다.
// (영화리스트의 경우 제목과 레이팅을 출력, 환자 리스트의 경우 환자 이름과 병명 출력)
// 따라서 출력해야할 구조체 멤버가 다르므로
// 이 부분은 main.c에서 당시에 구체화 되는 데이터 종류의 멤버들을 출력하는
// 함수를 따로 만들고 이 함수 포인터를 넘기는 식으로!
unsigned int WriteAllItems(const List* plist, FILE* file, void (*write_an_item_func)(FILE* file, Item item))
{
Node* pnode = plist->head;
unsigned int count = 0;
while (pnode != NULL) {
(*write_an_item_func)(file, pnode->item);
pnode = pnode->next;
count++;
}
return count;
}
unsigned int PrintAllItems(const List* plist, void (*print_an_item_func)(Item item))
{
Node* pnode = plist->head;
unsigned int count = 0;
while (pnode != NULL) {
printf("%d : ", count);
(*print_an_item_func)(pnode->item);
pnode = pnode->next;
count++;
}
return count++;
}
// 모든 노드를 돌면서 pfun 함수를 실행시킴
void Traverse(const List* plist, void (*pfun)(Item item))
{
Node* pnode = plist->head;
while (pnode != NULL) {
(*pfun)(pnode->item);
pnode = pnode->next;
}
}
// 같다고 찾아내는 기준 -> 비교함수 compare_func 주소 함수로 판단 (함수포인터)
bool Find(const List* plist, Item item_to_find, int* index, Item* item_found, bool (*compare_func)(Item a, Item b))
{
Node* pnode = plist->head;
*index = 0;
while (pnode != NULL) {
if ((*compare_func)(pnode->item, item_to_find) == true) {
*item_found = pnode->item;
return true;
}
pnode = pnode->next;
*index += 1;
}
return false;
}
void ClearList(List* plist)
{
Node* iterator = plist->head;
Node* temp = NULL;
while (iterator != NULL) {
temp = iterator->next;
free(iterator);
iterator = temp;
}
plist->head = NULL;
}
bool FindItemByIndex(const List* plist, const int index, Item** item)
{
Node* pnode = plist->head;
int count = 0;
while (pnode != NULL) {
if (count == index) break;
pnode = pnode->next;
count++;
}
if (pnode == NULL)
return false;
else {
*item = &pnode->item;
return true;
}
}
Node* FindNodeByIndex(const List* plist, const int index)
{
Node* pnode = plist->head;
int count = 0;
while (pnode != NULL) {
if (count == index) break;
pnode = pnode->next;
count++;
}
return pnode;
}
void InsertbyIndex(Item item, List* plist, int index)
{
Node* prev = FindNodeByIndex(plist, index - 1);
if (prev == NULL)
InsertItemFront(item, plist);
else
AppendItem(item, prev);
}
void RemoveFirstItem(List* plist)
{
Node* temp = NULL;
if (plist->head != NULL)
temp = plist->head->next;
free(plist->head);
plist->head = temp;
}
void RemoveNextItem(Node* prev) // prev 다음에 있는 아이템 삭제
{
Node* temp = NULL;
if (prev->next != NULL)
temp = NULL;
else
temp = prev->next->next;
free(prev->next);
prev->next = temp;
}
void RemoveByIndex(List* plist, int index)
{
Node* prev = FindNodeByIndex(plist, index - 1); // 삭제할 인덱스에 위치한 노드의 이전 노드
if (prev == NULL) // when index is 0
RemoveFirstItem(plist);
else
RemoveNextItem(prev);
}
// Item 이 뭔지 모르는 상태여야
unsigned int ReadFromFile(List* plist, const char* filename, bool (*read_an_item_func)(FILE* file, Item* item))
{
FILE* file = fopen(filename, "r");
if (file == NULL) {
printf("ERROR: Cannot open file.\n");
exite(1);
}
int num;
if (fscanf(file, "%d%*c", &num) != 1) {
printf("ERROR: Wrong file format.\n");
exite(1);
}
for (int n = 0; n < num; ++n) {
Item new_item;
const bool flag = read_an_item_func(file, &new_item);
if (flag == false) {
printf("ERROR: Wrong file format.\n");
exite(1);
}
else
AddItem(new_item, plist);
fclose(file);
return num;
}
}
unsigned int FindAndRun(const List* plist, Item item_to_find, bool (*compare_func)(Item a, Item b), void (*func_run)(Item item))
{
Node* pnode = plist->head;
int count = 0;
while (pnode != NULL) {
if ((*compare_func)(pnode->item, item_to_find) == true)
(*func_run)(pnode->item);
pnode = pnode->next;
count += 1;
}
return count;
}
📜main.c
#include "SimpleList.h"
bool read_an_item_func(FILE* file, Item* new_item);
void print_an_item(Item item);
bool name_starts_with(Item a, Item b);
bool joined_in(Item a, Item b);
int main() {
List avengers;
InitializeList(&avengers);
/*
1. Read data from a file.
2. Print all members.
3. Print all members whose names start with "Black"
4. Print all members who joined in 1965.
*/
const int n_items = ReadFromFile(&avengers, "avengers.txt", &read_an_item_func);
printf("%d avengers joined.\n", n_items);
printf("\nPrint all members\n");
PrintAllItems(&avengers, &print_an_item);
printf("\nPrint all members whose names start with \"Black\" : \n");
Item item_to_find;
strcpy(item_to_find.name, "Black");
FindAndRun(&avengers, item_to_find, &name_starts_with, &print_an_item);
printf("\nPrint all members who joined in 1965:\n");
item_to_find.year = 1965;
FindAndRun(&avengers, item_to_find, &name_starts_with, &print_an_item);
return 0;
}
bool read_an_item_func(FILE* file, Item* new_item)
{
if (fscanf(file, "%[^\n]%*c", new_item->name) != 1 ||
fsacnf(file, "%d%*c", &new_item->year) != 1) {
return false;
}
else
return true;
}
void print_an_item(Item item)
{
printf("\"%s\" joined in %d\n", item.name, item.year);
}
bool name_starts_with(Item a, Item b)
{
return strncmp(b.name, a.name, strlen(b.name)) == 0;
}
bool joined_in(Item a, Item b)
{
return a.year == b.year;
}
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기