Tópicos em Bancos de Dados: Sistemas de Informação
Inteligentes 1997/2
Implementação dos tipos de dados
Vamos discutir como implementar os tipos de dados definidos para a aplicação
usando a linguagem de definição de interfaces para RPC e
depois como estas definições são implementadas usando
a linguagem C. A seguir apresentamos o código necessário
para manipular estas estruturas de dados.
Definição da interface:
O documento anterior apresenta os tipos de dados que são usados
por este servidor, numa notação independente de implementação.
Vamos discutir agora como esta interface é implementada usando RPC
(Remote Procedure Call) e o protocolo XDR (External Data Representation).
Estas definições devem ficar num arquivo específico
para este fim, que posteriormente é compilado (usando o rpcgen)
para gerar os arquivos a serem incluídos nos servidores e nos clientes.
O protocolo XRD é usado para converter os dados do formato específico
de cada máquina para um formato único antes de transmiti-los
pela rede. Desta forma não precisamos nos preocupar de maneira nenhuma
com conversão de dados entre estações.
O arquivo contendo todas as definições do servidor é
o trec.x. É bom que se tenha muito cuidado ao manusear este
arquivo, pois quaiquer modificações nele quando compiladas
podem impedir a correta utilização do servidor.
Para a implementação deste servidor criamos os seguintes
tipos de dados básicos:
typedef int documento;
typedef int termo;
typedef float idf;
typedef short frequencia;
typedef float norma;
typedef string texto<>;
Desta forma, dentro dos programas deve-se utilizar sempre estes tipos de
dados para evitar quaisquer problemas de incompatibilidade. Note que o
tipo texto é um string de tamanho indefinido (<>
é a notação para indicar um vetor ilimitado).
Além destes tipos básicos foram criados tipos estruturados
para implementar as definições apresentadas. A principal
observação é o fato de que não existe uma estrutura
para representar conjuntos em C. Usando RPC podemos criar vetores (arrays)
ilimitados que foram usados como mecanismo para implementar os conjuntos.
O exemplo abaixo define o tipo lista invertida (lista_inv):
/* tipo da tupla (documento, frequencia) */
struct doc_freq{
documento d;
frequencia f;
};
typedef struct doc_freq ldf<>; /* vetor ilimitado de tuplas
*/
/* tipo lista_inv */
struct lista_inv_{
termo t; /* identificador do termo */
ldf lista; /* lista de ocorrencias do termo */
};
typedef struct lista_inv_ lista_inv;
Note a definição do tipo intermediário ldf
que é a lista de tuplas documento, frequência. Note também
que todas os tipos definidos têm um correspondente usando a terminologia
de RPC.
Como usar vetores ilimitados com RPC
Todas as definições apresentadas acima estavam no âmbito
da linguagem específica de RPC. Após compilar aquele código
temos em mãos uma série de definições na linguagem
C para desenvolver nossos programas. Vamos discutir como utilizar o código
e como acessar as estruturas de dados definidas.
O código gerado para implementar os vetores ilimitados em C é
um tanto obscuro e precisa ser bem compreendido. Vamos ilustrar dois exemplos:
a lista de termos, que é um caso típico.
Lista de termos
A definição no arquivo trec.x para a lista de termos
segue abaixo:
Veja que criamos uma estrutura para conter o vetor de termos. Isto é
uma boa prática usando RPC. Deve-se sempre procurar enviar dados
dentro de uma estrutura, ao invés de uma lista de estruturas.
Vamos analisar agora o código C gerado, que efetivamente será
manipulado por nossos programas.
struct lista_ter {
struct {
u_int t_len;
termo *t_val;
} t;
};
typedef struct lista_ter lista_ter;
Note que o vetor t criado na definição RPC
é implementado por um struct contendo dois campos. O primeiro
(t_len) contém o número de elementos do vetor (não
seu tamanho em bytes). O segundo campo (*t_val) contém
os valores propriamente ditos. Note que esta campo é do tipo termo.
Como escrever e como ler num vetor ilimitado:
Precisamos de uma variável ponteiro do tipo termo para
"percorrer" o campo t_val. O trecho de código a seguir
preenche hipoteticamente uma lista com n termos:
termo *t;
lista_ter lt;
/* inicializando a lista */
lt.t_len = n;
/* quantidade de elementos na lista */
lt.t_val = (termo *)malloc(n * sizeof(termo));
/* tamanho da lista */
t = lt.t_val; /* t aponta para o primeiro termo
dentro da lista */
...
for(i=0; i<n; i++){
escreve (*t);
/* escrita do valor do termo */
t++;
/* avanca para o proximo termo na lista */
}
Para ler o conteúdo de uma lista de termos fazemos um procedimento
análogo:
termo *t;
lista_ter lt;
n = lt.t_len;
/* pega a quantidade de elementos da lista */
t = lt.t_val; /* t aponta para o primeiro termo
dentro da lista */
...
for(i=0; i<n; i++){
le(*t);
/* leitura do valor de um termo */
t++;
/* avanca para o proximo termo da lista */
}
Algumas observações importantes:
-
Note que não é necessário inicializar uma estrutura
quando a recebemos através de uma chamada ao servidor. O código
embutido já faz esse trabalho para nós.
-
As estruturas que são passadas para e recebidas do servidor são
sempre referenciadas através de ponteiros então deve-se observar
atentamente quando usar o operador . e quando usar o operador
->.
Código de acesso às estruturas
Juntamente com o codigo para a implementacao de novos clientes, segue o
código fonte de cinco programas simples que acessam os cinco serviços
implementados no servidor. Observe os trechos referentes à escrita
e à leitura das estruturas. A tabela abaixo resume o conteúdo
de cada um dos arquivos.
Veja como pegar e utilizar estes e outros arquivos.
cinv.c |
programa que exibe a lista invertida de uma palavra. |
cfnt.c |
programa para exibir o texto fonte de um documento. |
ctermos.c |
programa par exibir a lista de termos (com respectivas frequências)
de um documento |
cidf.c |
programa para exibir uma lista de idfs correspondente a uma lista de
termos |
cnor.c |
programa para exibir uma lista de normas correspondente a uma lista
de documentos |
Acredito que a análise do código acima deve servir como
um bom ponto de partida para a implementação dos classificadores.
Sugiro também a compilação dos mesmos para fins de
melhor visualização da TREC, além de servir como apoio
para a verificação de seus programas.