Chater 3-2. 추상화된 리스트로 구체화한 영화 평점 리스트 (연결리스트 사용)

Date:     Updated:

카테고리:

태그:

홍정모 교수님의 강의 홍정모의 따라하며 배우는 C언어(부록) 를 듣고 정리한 필기입니다. 😀
ppt 캡처는 모두 교수님 강의에서 캡처한 것임을 밝힙니다.

Chapter 3. 추상화

🚀 인터페이스의 필요성

image

image

“어떤 데이터 아이템을 담을 수 있는 리스트”로서 추상화 한다. 👉 일반화!! 영화 평점 뿐만 아니라 어떤 데이터든 담을 수 있는 리스트로서 일반화하기. 모든 아이템 리스트들이 가질 수 있는 공통된 "객체"와 "연산"을 정리하여 일반화할 수 있겠다.

  • 1️⃣ “객체” 일반화 (OOP 개념에서 보자면 멤버 변수)
  • 2️⃣ “연산” 일반화 (OOP 개념에서 보자면 추상 함수)


🚀 추상화 과정

image

  • 1️⃣ 데이터와 자료 구조의 분리
    • 영화 데이터만 담을 수 있는 리스트에서 모든 종류의 데이터를 담을 수 있는 리스트로서 일반화 해야 하기 떄문
    • 환자 리스트, 영화 리스트, 쇼핑 리스트 같은 다양한 데이터를 담는 리스트로 구체화 될 수 있도록 “리스트” 라는 추상적인 개념을 일반화하는 것이기 때문에 데이터와 분리해야한다.
    • 이렇게 분리하면 구체화할 데이터를 다른 것으로 바꿀 때 쉽게 수정이 가능하다.
      • 영화 평점 리스트에서 쇼핑 리스트로 쉽게 바꿀 수 있음!

image

  • 2️⃣ 코드 재사용
    • 왼쪽 코드가 오른쪽 코드로 간단해질 수 있다.


🚀 영화 평점 리스트 일반화하기 (어떤 리스트로든 구체화 될 수 있도록!)

영화 평점 관리 프로그램 코드 참고

image

“리스트”자체를 일반화 해놓으면 나중에 영화 리스트를 환자 리스트로 바꾸더라도 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;
}


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

DataStructure2 카테고리 내 다른 글 보러가기

댓글 남기기