Estruturas de Dados Genéricas
Leonardo ReisDepartamento de Computação e Sistemas Universidade Federal de Ouro Preto
Introdução
Para cada estrutura de dados (Lista, Fila, Pilha, . . . ) com tipos diferentes teremos que fazer uma nova implementação?
Introdução
O que altera de uma implementação para outra?
I Exemplo: Lista de int e Lista de Contatos
I Como mudam: interface do TAD, definição dos tipos e implementação das
funções?
Ideal: estrutura de dados que possa ser instanciada com qualquer tipo, sem necessidade de retrabalho (implementar novamente)!
1 s t r u c t lista { 2 i n t size; 3 s t r u c t node∗ head; 4 } ; 5 6 s t r u c t node { 7 Contato item; 8 s t r u c t node∗ next; 9 } ; 1 s t r u c t lista { 2 i n t size; 3 s t r u c t node∗ head; 4 } ; 5 6 s t r u c t node { 7 i n t item; 8 s t r u c t node∗ next; 9 } ;
Introdução
O que altera de uma implementação para outra?
I Exemplo: Lista de int e Lista de Contatos
I Como mudam: interface do TAD, definição dos tipos e implementação das
funções?
Ideal: estrutura de dados que possa ser instanciada com qualquer tipo, sem necessidade de retrabalho (implementar novamente)!
1 i n t l_pos(Lista l, Contato c) {
2 i n t i= 0;
3 s t r u c t node∗ aux= l−>head;
4 while(aux != NULL) {
5 i f(c_equals(aux−>item, c) )
6 return i; / / achou
1 i n t l_pos(ListaInt l, i n t e) {
2 i n t i = 0;
3 s t r u c t node∗ aux= l−>head;
4 while(aux != NULL) {
5 i f(aux−>item==e)
Introdução
O que altera de uma implementação para outra?
I Exemplo: Lista de int e Lista de Contatos
I Como mudam: interface do TAD, definição dos tipos e implementação das
funções?
Ideal: estrutura de dados que possa ser instanciada com qualquer tipo, sem necessidade de retrabalho (implementar novamente)!
1 i n t l_pos(Lista l, Contato c) {
2 i n t i= 0;
3 s t r u c t node∗ aux= l−>head;
4 while(aux != NULL) {
5 i f(c_equals(aux−>item, c) )
6 return i; / / achou 7 aux= aux−>next; i++;
8 }
9 return −1; / / não achou 10 }
1 i n t l_pos(ListaInt l, i n t e) {
2 i n t i = 0;
3 s t r u c t node∗ aux= l−>head;
4 while(aux != NULL) {
5 i f(aux−>item==e)
6 return i; / / achou 7 aux =aux−>next; i++;
8 }
9 return −1; / / não achou 10 }
Estruturas Genéricas em C
A linguagem Cnão prover mecanismos explícitospara definir tipos de dados genéricos, tais como as linguagens C++ e Java
Alternativa de Implementação em C:
I uso de ponteiros para qualquer informação (desconsidera o tipo):void*
I manipulação das informações usando funções fornecidas pelo usuário da
estrutura:ponteiros para funções
I cliente fica responsável por converter para o tipo específico
I desvantagem: perde-se a informação do tipo e fica mais propenso a erros
Ponteiros Para Funções
Declaração de ponteiros para funções:return_type (*var_name) (list_param)
I Exemplo: int (*pf) (int, int);
Inicialização de ponteiros de funções:var_name = func_name
I Exemplo: pf = soma; ou pf = &soma;
Uso de ponteiros de funções:var_name (list_param)
I Exemplo: pf(1,2)
Exemplo
1 i n t soma(i n t a, i n t b) { 2 return a+ b; 3 } 1 i n t main( ) { 2 i n t (∗pf) (i n t, i n t) ; 3 pf=soma; / / pf = &soma; 4 printf("%d \ n", pf(1 ,2) ) ; 5 return 0; 6 }Estudo de Caso: Pilha Genérica
“transformar” a implementação de Pilha de inteiros em uma implementação genérica
modificar o tipo dos elementos armazenados para void* analisar cada função:
I se não acessa os dados armazenados, então não há o que modificar
I se acessa dados, então usar ponteiros de funções para prover acesso
Pilha Genérica: Definição do Tipo Estruturado
1 s t r u c t pilha { 2 i n t size; 3 s t r u c t node∗ top; 4 } ; 5 6 s t r u c t node { 7 i n t item; 8 s t r u c t node∗ next; 9 } ;Pilha Genérica: Definição do Tipo Estruturado
1 s t r u c t pilha { 2 i n t size; 3 s t r u c t node∗ top; 4 } ; 5 6 s t r u c t node { 7 i n t item; 8 s t r u c t node∗ next; 9 } ; 1 s t r u c t pilha { 2 i n t size; 3 s t r u c t node∗ top; 4 } ; 5 6 s t r u c t node { 7 void∗ item; 8 s t r u c t node∗ next; 9 } ;Pilha Genérica: Funções de Adição e Remoção
Item adicionado e removido é genérico
1 void s_push(PilhaInt s, i n t e) {
2 s t r u c t node∗ novo= (s t r u c t node∗)
3 malloc(sizeof(s t r u c t node) ) ;
4 novo−>item= e;
5 novo−>next= s−>top;
6 s−>top=novo; 7 s−>size++; 8 } 1 i n t s_pop(PilhaInt s) { 2 i f(s_isEmpty(s) ) 3 exit( 1 ) ; / / ERROR 4 s t r u c t node∗ n= s−>top; 5 i n t e= n−>item; 6 s−>top= n−>next; 7 free(n) ; 8 s−>size−−; 9 return e; 10 }
Pilha Genérica: Funções de Adição e Remoção
Funções modificadas:
1 void s_push(Pilha s, void∗ e) {
2 s t r u c t node∗ novo= (s t r u c t node∗)
3 malloc(sizeof(s t r u c t node) ) ;
4 novo−>item= e;
5 novo−>next= s−>top;
6 s−>top=novo;
7 s−>size++;
8 }
1 void∗ s_pop(Pilha s) {
2 i f(s_isEmpty(s) ) 3 exit( 1 ) ; / / ERROR 4 s t r u c t node∗ n= s−>top; 5 void∗ e= n−>item; 6 s−>top= n−>next; 7 free(n) ; 8 s−>size−−; 9 return e; 10 }
Pilha Genérica: Funções isEmpty e size
Adequar o nome do tipo
1 i n t s_isEmpty(PilhaInt s) { 2 return s−>size== 0 ? 1 : 0; 3 } 1 i n t s_size(PilhaInt s) { 2 return s−>size; 3 }
Pilha Genérica: Funções isEmpty e size
Funções modificadas: 1 i n t s_isEmpty(Pilha s) { 2 return s−>size== 0 ? 1 : 0; 3 } 1 i n t s_size(Pilha s) { 2 return s−>size; 3 }Pilha Genérica: Funções de Criação e Remoção
Função de criação: adequar o nome do tipo
Função de remoção: liberar espaço do item na memória. Como? I função fornecida pelo usuário, como parâmetro
1 PilhaInt s_new( ) {
2 PilhaInt s = (PilhaInt)
3 malloc(sizeof(s t r u c t pilha) ) ;
4 s−>size= 0;
5 s−>top=NULL;
6 return s;
7 }
1 void n_free(s t r u c t node∗ n) {
2 i f(n ==NULL) 3 return; 4 n_free(n−>next) ; 5 free(n) ; 6 } 7
8 void s_free(PilhaInt s) {
9 n_free(s−>top) ;
10 free(s) ;
Pilha Genérica: Funções de Criação e Remoção
Função de criação: adequar o nome do tipo
Função de remoção: liberar espaço do item na memória. Como?
I função fornecida pelo usuário, como parâmetro
1 PilhaInt s_new( ) {
2 PilhaInt s = (PilhaInt)
3 malloc(sizeof(s t r u c t pilha) ) ;
4 s−>size= 0;
5 s−>top=NULL;
6 return s;
7 }
1 void n_free(s t r u c t node∗ n) {
2 i f(n ==NULL) 3 return; 4 n_free(n−>next) ; 5 free(n) ; 6 } 7
8 void s_free(PilhaInt s) {
Pilha Genérica: Funções de Criação e Remoção
Funções modificadas:
1 Pilha s_new( ) {
2 Pilha s = (Pilha)
3 malloc(sizeof(s t r u c t pilha) ) ;
4 s−>size= 0;
5 s−>top=NULL;
6 return s;
7 }
1 voidn_free(s t r u c t node∗ n,
2 void (∗fn) (void∗) ) { 3 i f(n==NULL) return; 4 n_free(n−>next, fn) ; 5 fn(n−>item) ; 6 free(n) ; 7 } 8
9 voids_free(Pilha s,
10 void (∗fn) (void∗) ) {
11 n_free(s−>top, fn) ;
12 free(s) ;
Pilha Genérica: Definição da Interface (TAD)
Adequar o nome do tipo: de PilhaInt para Pilha
Modificar as funções para receber / retornar o item genérico: void* Corrigir a assinatura das operações que precisam receber ponteiros de funções como parâmetros
1 # i f n d e f PILHAINT_H_INCLUDED
2 #define PILHAINT_H_INCLUDED
3
4 typedef s t r u c t pilha∗ PilhaInt;
5
6 PilhaInt s_new( ) ;
7 void s_free(PilhaInt) ;
8 void s_push(PilhaInt, i n t) ;
Pilha Genérica: Definição da Interface (TAD)
Definição atualizada:
1 # i f n d e f PILHA_H_INCLUDED
2 #define PILHA_H_INCLUDED
3
4 typedef s t r u c t pilha∗ Pilha;
5
6 Pilha s_new( ) ;
7 void s_free(Pilha, void (∗fn) (void∗) ) ;
8 void s_push(Pilha, void∗) ;
9 void∗ s_pop(Pilha) ;
10 i n t s_isEmpty(Pilha) ;
11 i n t s_size(Pilha) ;
12
Pilha Genérica: Pilha de Inteiros
1 #include " s t d i o . h"
2 #include " s t d l i b . h"
3 #include " Pilha . h"
4
5 void freeInt(void∗ e) {
6 i f(e ==NULL) return; 7 free(e) ; 8 } 9 10 i n t main( ) { 11 Pilha s=s_new( ) ; 12 i n t∗ item;
13 item=(i n t∗)malloc(sizeof(i n t) ) ;
14 ∗item= 5;
17 ∗item= 7;
18 s_push(s, item) ;
19 item=(i n t∗)malloc(sizeof(i n t) ) ;
20 ∗item= 3;
21 s_push(s, item) ;
22 item=(i n t∗)malloc(sizeof(i n t) ) ;
23 ∗item= 2; 24 s_push(s, item) ; 25 26 while(s_isEmpty(s) == 0) { 27 i n t∗ e= (i n t∗) s_pop(s) ; 28 printf("%d ", ∗e) ; 29 } 30 printf(" \ n") ;
Relembrando: TAD Contato
1 # i f n d e f CONTATO_H_INCLUDED
2 #define CONTATO_H_INCLUDED
3
4 typedef s t r u c t contato∗ Contato;
5
6 Contato c_new(char∗, char∗, char∗) ;
7 void c_free(Contato) ;
8 void c_print(Contato) ;
9 i n t c_equals(Contato, Contato) ;
10
11 char∗ c_getNome(Contato) ;
12 char∗ c_getSobreNome(Contato) ;
13 char∗ c_getFone(Contato) ;
14
15 void c_setNome(Contato, char∗) ;
16 void c_setSobreNome(Contato, char∗) ;
17 void c_setFone(Contato, char∗) ;
18
Pilha Genérica: Pilha de Contatos
1 #include " s t d i o . h" 2 #include " Contato . h" 3 #include " Pilha . h" 4 5 i n t main( ) { 6 Pilha s =s_new( ) ;7 s_push(s, c_new(" Leonardo ", " Reis ", "9999−9999") ) ;
8 s_push(s, c_new("Emmanuel", " V i e i r a ", "8888−8888") ) ;
9 s_push(s, c_new(" Helen ", "da Silva ", "7777−7777") ) ;
10
11 while(s_isEmpty(s) == 0) {
12 Contato e = (Contato) s_pop(s) ;
13 printf("%s %s : %s \ n",c_getNome(e) , c_getSobreNome(e) , c_getFone(e) ) ;