티스토리 뷰
주의 사항!
- 이 글은 제가 직접 공부하는 중에 작성되고 있습니다.
- 따라서 제가 이해하는 그대로의 내용이 포함됩니다.
- 따라서 이 글은 사실과는 다른 내용이 포함될 수 있습니다.
드디어 첫 번째 자료구조를 배웁니다. 그런데 이에 앞서 '추상 자료형'이라는 용어를 먼저 소개하고자 합니다. 참고로 이 용어의 개념이 모호하다 하여 이에 대한 이해 없이 자료구조를 공부하는 경우도 있는데 이런 일은 없어야 한다고 저자는 말합니다.
문제를 하나 낼 테니 문제에서 설명하는 것이 무엇인지 맞춰보기 바랍니다.
- 여기에는 신용카드나 면허증과 같은 카드들을 넣고 뺄 수 있습니다.
- 여기에는 지폐나 동전도 넣을 수 있습니다.
이 문제의 답은 지갑입니다. 아마 다른 분들도 위 문제를 보고 지갑을 연상하는 데에 어려움은 없었을 것이라 생각합니다.
그럼 여기에서 한 번 생각해 보겠습니다. 위 문제에는 설명하는 대상이 지갑이라고 말하지는 않았습니다. 그런데 대부분 어렵지 않게 지갑을 생각했을 것입니다. 위 문제들을 보고 지갑을 생각할 수 있었던 이유는 바로 위 문제에 나열된 것들이 바로 지갑의 '추상 자료형'이기 때문입니다.
추상 자료형이란 위와 같이 지갑의 기능들을 나열한 것을 말합니다. 그런데 위 문제에서 지폐를 넣고 뺄 수 있다고 설명했지만, 단순히 지폐를 넣고 빼는 데에도 다음과 같은 과정이 필요합니다.
- 재킷 안주머니에서 지갑을 꺼낸다
- 지갑을 펼친다.
- 지폐를 넣기 쉽게 지폐 넣는 곳을 벌린다.
- 지폐를 가지런히 하고 넣는다.
- 지갑을 닫는다.
- 지갑을 안주머니에 넣는다.
그런데 문제에서는 위와 같이 지폐를 넣고 빼기 위해 어떤 과정이 필요한지는 설명하지 않았습니다. 즉, 좀 더 자세히 말해 추상 자료형은 무엇인가의 기능들을 나열하되 그 기능들이 어떻게 구현되는지 자세한 과정은 설명하지 않은 것을 말합니다.
저는 위 설명을 보고 구조체를 생각했습니다. 지갑의 기능들을 나열한 것이 추상 자료형이라니 다음과 같은 관계가 얼핏 떠올랐습니다.
struct Wallet
{
int TaekOutMoney(...);
void PutMoney(...);
};
위 구조체는 지갑을 표현합니다. 다만 멤버에는 돈을 넣거나 빼는 기능을 구현한 함수들이 선언되어 있습니다. 물론 어떤 과정을 거쳐 돈을 넣고 빼는지는 설명하지 않으니 함수의 몸체는 보이지 않습니다.
물론 위와 같은 구조는 불가능합니다. C언어의 구조체는 함수를 멤버로 가질 수 없습니다. 더군다나 함수의 선언만 돼어 있을 뿐 정의가 되지 않았습니다. 그럼에도 위와 같은 코드를 만들어 본 이유는 추상 자료형이 구조체와 얼추 비슷한 면이 있다는 생각에서였습니다.
다음은 실제 정의가 가능한 지갑 구조체입니다.
typedef struct
{
int coin100Num; //100원 짜리 동전의 개수
int bill10000Num; //10,000원짜리 지폐의 개수
} Wallet;
위 구조체는 백원짜리 동전의 개수와 만 원짜리 지폐의 개수를 저장합니다. 적어도 C언어에서 위와 같은 구조체의 정의는 완성된 정의입니다. 하지만 컴퓨터 공학적 측면에서 보면 위의 구조체의 정의는 완성된 정의가 아닙니다(10원, 500원, 5000원권 개수를 저장할 변수를 만들지 않아서가 아닙니다). Wallet을 기반으로 하는 연산의 종류를 결정하는 것도 자료형 정의의 일부로 보아야 하고, 이러한 연산의 종류가 결정되었을 때 자료형의 정의는 완성됩니다.
즉, 위 구조체는 지갑 안에 각종 동전이 몇 개, 지폐가 몇 장 들어 있는지 저장하는 변수만 있을 뿐, 지갑이라는 이 구조체가 어떤 일을 할 수 있고, 어떤 기능을 가져야 하는지는 명시되어 있지 않기 때문에 완성된 정의라고 볼 수 없다는 것입니다. 물건을 구매할 때 물건의 금액만큼 지갑에 돈을 담아 카운터에 줘야 하는 것인지, 지갑에 동전이나 지폐를 잔뜩 모아다가 나중에 은행에 가서 잔돈 교환할 때 쓰는 것인지, 이 지갑이라는 것을 어떻게 사용해야 하는지는 도통 알 수가 없기 때문에 완성된 정의로 볼 수는 없습니다.
(C++의 클래스는 클래스 내에 멤버함수를멤버 함수를 통해서 해당 클래스가 어떤 기능을 가지는지까지 명시할 수 있으므로 같은 관점에서 보면 완성된 정의라고 볼 수 있겠습니다. 정말로 그렇다면 '추상 자료형'이라고 하는 것은 C언어의 구조체가 C++의 클래스와 같이 멤버 함수를 가질 수 있게 하기 위한 개념인지 궁금해집니다. 정말로 그렇다면 추상 자료형이라는 개념은 C언어에서만 등장하는 것인지도 궁금합니다.)
C언어의 구조체는 함수를 멤버로 가질 수 없기 때문에 구조체가 가져야 할 기능을 다음과 같이 전역함수의 형태로 정의합니다.
int TackOutMoney(Wallet* pw, int coinNum, int billNum);
void PutMoney(Wallet* pw, int coinNum, int billNum);
그리고 위 두 함수가 Wallet이 가져야 할 기능의 전부라고 한다면, 이로써 Wallet에 대한 자료형의 정의는 완성됩니다.
다시 본론으로 돌아와서 Wallet의 추상 자료형을 정의해 보겠습니다.
int TackOutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에서 돈을 꺼낸다
//두 번째 인자로 꺼낼 동전의 개수, 세 번째 인자로 꺼낼 지폐의 수를 전달한다.
//꺼내고자 하는 돈의 총액이 반환된다. 그리고 그만큼 돈은 차감된다.
void PutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에 돈을 넣는다.
//두 번째 인자로 넣을 동전의 수, 세 번째 인자로 넣을 지폐의 수를 전달한다.
//넣은 만큼 동전과 지폐의 수가 증가한다.
위의 추상 자료형을 보고 잠깐 생각해 볼 것이 있습니다. 지금은 앞서 정의했던 Wallet 구조체를 우리가 기억하고 있기 때문에 위 추상 자료형을 보면 구조체 Wallet이 어떤 식으로 기능할지 쉽게 상상할 수 있습니다. 하지만 실제로 위와 같은 추상 자료형을 볼 때는 이 추상 자료형이 무엇에 대한 추상 자료형인지 모른 채로 봐야 합니다.
마치 앞서 예로 들었던 문제를 마주하듯이 말입니다.
'신용카드나 면혀증, 신분증 같은 카드를 넣고 뺄 수 있다. 지폐와 동전도 넣고 뺄 수 있다.'까지가 추상 자료형에 해당하는 부분이지 '이것은 지갑이고, 신용카드, 면허증, 신분증, 지폐, 동전, 상품권, 쿠폰, 수표 등을 저장할 수 있다'고 말해주지는 않습니다.
그런데 사실 '이것은 지갑이고, 신용카드, 면허증, 신분증, 지폐, 동전, 상품권, 쿠폰, 수표 등을 저장할 수 있다'는 알 필요가 없습니다. '신용카드나 면허증, 신분증 같은 카드를 넣고 뺄 수 있다. 지폐와 동전도 넣고 뺄 수 있다.'는 추상 자료형만 보고도 '이것은 지갑이구나 그리고 신용카드, 면허증, 신분증, 지폐, 동전을 저장할 수 있겠구나'정도는 쉽게 이해할 수 있기 때문입니다.
그런데 정말 사실은 '이것은 지갑이구나 그리고 신용카드, 면허증, 신분증, 지폐, 동전을 저장할 수 있겠구나'조차도 이해하지 않아도 됩니다. '신용카드나 면혀증, 신분증 같은 카드를 넣고 뺄 수 있다. 지폐와 동전도 넣고 뺄 수 있다.'는 추상 자료형을 보고 이것이 당최 무엇에 대한 것인지 하나도 이해하지 못하더라고 우리는 그저 이것을 신용카드나 신분증, 면허증을 넣고 빼는 기능, 지폐나 동전도 넣고 빼는 일에만 그냥 가져다 쓰면 됩니다. 왜냐하면 어차피 이것이 무엇이든지 간에 이것이 가지고 있는 기능은 카드와 지폐, 동전을 넣고 빼는 것이 다이며, 이것이 콘크리트 블록이 됐든 주전자가 됐든 이것에 카드, 지폐 동전을 넣고 빼는 일이 가능하다고 말해주는 것이기 때문에 그대로만 사용한다면 전혀 문제 될 것이 없기 때문입니다. 나중에 가서 왜 콘크리트 블록에 신용카드, 지폐를 넣었냐 누군가 따지더라도 '네가 그렇게 해도 된다고 추상 자료형으로 명시하지 않았냐!'하고 따지면 될 일입니다.
정리하자면, 물론 다음과 같이 친절하게 무엇에 대한 추상 자료형인지까지도 추상 자료형을 통해 알려줄 수 있지만,
typedef struct
{
int coin100Num;
int bill10000Num;
} Wallet;
int TackOutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에서 돈을 꺼낸다
//두 번째 인자로 꺼낼 동전의 개수, 세 번째 인자로 꺼낼 지폐의 수를 전달한다.
//꺼내고자 하는 돈의 총액이 반환된다. 그리고 그만큼 돈은 차감된다.
void PutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에 돈을 넣는다.
//두 번째 인자로 넣을 동전의 수, 세 번째 인자로 넣을 지폐의 수를 전달한다.
//넣은 만큼 동전과 지폐의 수가 증가한다.
이는 전혀 불필요한 일이라는 것이며, 따라서 추상 자료형은 다음과 같이 무엇인가에 대한 기능만 나열하는 것이 옳다는 것입니다.
int TackOutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에서 돈을 꺼낸다
//두 번째 인자로 꺼낼 동전의 개수, 세 번째 인자로 꺼낼 지폐의 수를 전달한다.
//꺼내고자 하는 돈의 총액이 반환된다. 그리고 그만큼 돈은 차감된다.
void PutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에 돈을 넣는다.
//두 번째 인자로 넣을 동전의 수, 세 번째 인자로 넣을 지폐의 수를 전달한다.
//넣은 만큼 동전과 지폐의 수가 증가한다.
물론 이 예만 보고 다음과 같이 반문할 수도 있습니다.
"지갑 안에 100원짜리 동전이 몇 개가 들어 있는지 모르지 않냐"
지갑 안에 100원짜리 동전이 몇 개가 들어 있는지 궁금하면, 다음과 같이 지갑 안의 100원짜리 동전의 개수를 가져오는 기능을 추가하면 될 일입니다.
int TackOutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에서 돈을 꺼낸다
//두 번째 인자로 꺼낼 동전의 개수, 세 번째 인자로 꺼낼 지폐의 수를 전달한다.
//꺼내고자 하는 돈의 총액이 반환된다. 그리고 그만큼 돈은 차감된다.
void PutMoney(Wallet* pw, int coinNum, int billNum)
//첫 번째 인자로 전달된 주소의 지갑에 돈을 넣는다.
//두 번째 인자로 넣을 동전의 수, 세 번째 인자로 넣을 지폐의 수를 전달한다.
//넣은 만큼 동전과 지폐의 수가 증가한다.
int GetCoin100Num()
//지갑 안의 100원짜리 동전의 개수를 반환한다.
아! 혹여 혼란이 생길까 봐 말하자면, 실제 추상 자료형의 정의를 저렇게 하지는 않을 것입니다. 어떤 식으로 추상 자료형을 정의하는지는 아직 책에서도 말해주지 않았습니다. 차차 두고 봐야 할 것 같습니다.
'공부 일지 > 자료구조 공부 일지' 카테고리의 다른 글
Chapter 03. 리스트의 활용 연습문제 1 (0) | 2021.03.08 |
---|---|
Chapter 03. 배열을 이용한 리스트의 구현 (0) | 2021.03.08 |
Chapter 02. 하노이 타워 : The Tower of Hanoi (0) | 2021.03.08 |
Chapter 02. 재귀의 활용 (0) | 2021.03.07 |
Chapter 02. 함수의 재귀적 호출의 이해 (0) | 2021.03.07 |