Listas e Estruturas Pedro Barahona DI/FCT/UNL Maio 2004 11/12 de Maio de 2004 Estruturas 1 Processamento de Informação Alfanumérica • Como visto anteriormente, muita informação alfanumérica está registada em ficheiros. Numa base de dados da empresa, são mantidos ficheiros com informação sobre os empregados da empresa. • O processamento dessa informação pode ser feita através da leitura e escrita de ficheiros. No entanto estas operações são muito demoradas em geral, e seria preferível manter a informação em memória. cod 610 825 316 34 723 11/12 de Maio de 2004 nome Paulo Fernandes Lopes Pedro Vieira Marta Costa Martins Rui Vasco Pereira Jorge Barata Estruturas vencimento data 2341.36 989.24 1389.17 5310.32 767.26 15/04/1996 25/06/1999 05/01/1992 15/04/1996 03/09/2002 2 Processamento de Informação Alfanumérica • Podemos considerar que a informação destes ficheiros corresponde a uma tabela. Esta tabela é constituída por uma sequência de “registos”, um em cada linha. • Se toda a informação fosse numérica, isto é se cada registo tivesse n “campos”, ela poderia ser guardada numa matriz com m linhas, uma linha para cada registo de n posições. • Em Octave, existe um problema: uma posição de uma matriz não pode ser ocupada por uma cadeia de caracteres! cod 610 825 316 34 723 11/12 de Maio de 2004 nome Paulo Fernandes Lopes Pedro Vieira Marta Costa Martins Rui Vasco Pereira Jorge Barata Estruturas vencimento data 2341.36 989.24 1389.17 5310.32 767.26 15/04/1996 25/06/1999 05/01/1992 15/04/1996 03/09/2002 3 Processamento de Informação Alfanumérica • Desta forma a informação de uma tabela alfanumérica não pode ser guardada como uma matriz. Vamos ver como se podem ultrapassar estes problemas em Octave, fazendo-o em duas “fases”: – Como representar um registo heterogéneo, com vários campos de diferentes tipos, alguns alfanuméricos. – Como armazenar e aceder a vários destes registos heterogéneos. cod 610 825 316 34 723 11/12 de Maio de 2004 nome Paulo Fernandes Lopes Pedro Vieira Marta Costa Martins Rui Vasco Pereira Jorge Barata Estruturas vencimento data 2341.36 989.24 1389.17 5310.32 767.26 15/04/1996 25/06/1999 05/01/1992 15/04/1996 03/09/2002 4 Estruturas • Vectores e Matrizes são muito úteis quando os dados são todos do mesmo tipo (no Octave, de qualquer tipo numérico). • No entanto, em muitos casos, a informação que se pretende agrupar com um só identificador não é do mesmo tipo. • Por exemplo, um empregado duma empresa pode ter associado a seguinte informação – Um código (um número?) – Um nome (uma cadeia de caracteres) – Um vencimento (um decimal) – Uma data de entrada (uma cadeia, ou 3 campos numéricos, para o dia, mês e ano) cod nome 610 Paulo Fernandes Lopes 11/12 de Maio de 2004 Estruturas venc 2341.36 data 15/04/1996 5 Estruturas • As várias linguagens de programação permitem o agrupamento destes dados heterogéneos, com um mesmo identificador de uma forma variada (records no Pascal, Struct em C, ...) • O Octave adopta uma designação semelhante à do C, denominando estes agrupamentos como estruturas. • Consideremos pois o caso do empregado abaixo, em que gostaríamos de agregar toda a informação numa única variável, do tipo estrutura, que denotaremos como emp_610. emp_610 = 11/12 de Maio de 2004 cod nome 610 Paulo Fernandes Lopes Estruturas venc 2341.36 data 15/04/1996 6 Estruturas • As estruturas são compostas por vários campos, cada um com um nome. Na estrutura para representação da informação de empregados, consideraremos os campos abixo, que guardam a informação “esperada” – cod: o código do empregado – nome: o nome do empregado – venc: o vencimento do empregado – data: a data de entrada do empregado na empresa. emp_610 = 11/12 de Maio de 2004 cod nome 610 Paulo Fernandes Lopes Estruturas venc 2341.36 data 15/04/1996 7 Estruturas • Uma vez definidos os nomes dos campos da estrutura, podemos atribuir-lhe os valores pretendidos. • O acesso a um campo da estrutura é feito fazendo suceder ao nome da estarutura o nome do campo pretendido, separado por um ponto (‘.’). • Por exemplo, a atribuição dos 4 valores dos campos pode ser feita pelas seguintes atribuições: emp_610.cod = 610; emp_610.nome = “Paulo Fernandes Lopes”; emp_610.venc = 2341.36; emp_610.data=“15/04/1996”; emp_610 = 11/12 de Maio de 2004 cod nome 610 Paulo Fernandes Lopes Estruturas venc 2341.36 data 15/04/1996 8 Estruturas • De notar que os campos de uma estrutura não são ordenados, e podem ser preenchidos por qualquer ordem. • Assim a estrutura que temos referido emp_610 = cod nome 610 Paulo Fernandes Lopes venc 2341.36 data 15/04/1996 pode ser inicializada quer com a sequência de instruções empregado.data=“15/04/1996”; empregado.cod = 610; empregado.nome = “Paulo Fernandes Lopes”; empregado.venc = 2341.36; ou com outra sequeência empregado.venc = 2341.36; empregado.cod = 610; empregado.data=“15/04/1996”; empregado.nome = “Paulo Fernandes Lopes”; 11/12 de Maio de 2004 Estruturas 9 Estruturas • Uma vez agrupados os vários items de informação numa só variável do tipo estrutura, podemos referir alguns campos depois de verificar outros. • Por exemplo, dados vários empregados com o tipo referido, indicar qual o nome dos que ganham mais de 1000 euros. • Na sintaxe do Octave, tal poderia ser feito através da instrução condicional if emp_610.venc > 1000 then disp(emp_610.nome) endif • No entanto este tipo de processamento só é verdadeiramente útil se tivermos a possibilidade de aceder a todos os empregados de uma forma genérica. 11/12 de Maio de 2004 Estruturas 10 Estruturas • Por exemplo, se tivessemos uma tabela com várias linhas, com códigos na primeira coluna e vencimentos na 2ª coluna, poderíamos apresentar os códigos dos empregados com vencimento superior a 1000 euros através da seguinte 1 2 instrução iterativa: for i = 1:n if tabela(i,2) > 1000 then disp(tabela(i,1)) endif endfor; 1 2 3 4 5 ... 610 825 316 34 723 ... 2341.36 989.24 1389.17 5310.32 767.26 ... • Por analogia, o que é necessário é poder aceder a uma sequência de (1 a n) estruturas do tipo da do empregado. • Em Octave, essa sequência pode ser implementada através de listas. 11/12 de Maio de 2004 Estruturas 11 Listas • Uma lista é uma sequência de dados do mesmo tipo, simples ou complexo, para as quais estão definidas as operações de: • Criação: list(elem_1, elem_2, ..., elem_k) – Cria uma lista, com os elementos de 1 a k (ou uma lista vazia se k = 0) • Acrescento: append(nome_lista,elem_1, ...,elem_k) – Acrescenta os os elementos de 1 a k à lista com o nome indicado no 1º argumento • Acesso: nth(nome_lista, k) – Acede ao k-ésimo elemento da lista. De notar que esse elemento pode ser uma estrutura arbitrariamente complexa. 11/12 de Maio de 2004 Estruturas 12 Listas • Para ilustrar estes conceitos, vamos ler um ficheiro com informação sobre empregados e criar uma lista com essa informação. • A instrução principal consiste em criar uma estrutura, emp, e atribuir-lhe os valores lidos do ficheiro (a formatação dos campos é feita como anteriormente). [emp.cod, emp.nome, emp.venc, emp.data, count] = fscanf(f_aux,"%i%s%f%s","C"); • Para além destas instruções, são necessárias instruções para inicializar a lista e para a ir acrescentando com os empregados lidos. O número de empregados também é computado. 11/12 de Maio de 2004 Estruturas 13 Listas • Eis o programa completo, que cria uma lista, tab_empregados, com a informação sobre os empregados inicialmente no ficheiro “empresa_aux_var.txt”. [f_aux, msg] = fopen("empresa_aux_var.txt", "r"); tab_empregados = list(); n = 0; [emp.cod,emp.nome,emp.venc,emp.data, count] = fscanf(f_aux,"%i%s%f%s","C"); while !feof(f_aux) n = n+1; tab_empregados = append(tab_empregados, emp); [emp.cod,emp.nome,emp.venc,emp.data, count] = fscanf(f_aux,"%i%s%f%s","C"); endwhile; fclose(f_aux); 11/12 de Maio de 2004 Estruturas 14 Listas • A partir deste momento, todo o processamento da informação sobre os empregados pode ser feito sem leitura do ficheiro, mas apenas por acesso à lista “tab_empregados”. • Vamos ilustrar esta situação através do cálculo da média dos vencimentos dos empregados. • Começamos por apresentar a versão que obriga à leitura do ficheiro, mostrando seguidamente a versão que utiliza a lista já criada. 11/12 de Maio de 2004 Estruturas 15 Listas • O programa que lê o ficheiro é idêntico ao apresentado anteriormente. [f_aux, msg] = fopen("empresa_aux_var.txt", "r"); [cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C"); i = 0; total = 0; while !feof(f_aux) i = i+1; total = total +venc; [cod,nome, venc, data, count] = fscanf(f_aux,"%i%s%f%s","C"); endwhile; printf("o total dos vencimentos é de %7.2f \n", total); printf("a média dos vencimentos é de %7.2f \n", total/i); fclose(f_aux); 11/12 de Maio de 2004 Estruturas 16 Listas • Se já tiver sido lida a informação dos empregados para a lista “tab_empregados”, com n elementos, ela pode ser acedida directamente, sem necessidade de nova leitura do ficheiro. total = 0; for i = 1:n total = total + nth(tab_empregados,i).venc; endfor; printf("o total de vencimentos é %7.2f \n“, total); printf(“ e a sua média é %7.2f \n", total/n); 11/12 de Maio de 2004 Estruturas 17 Listas • Igualmente se podem escrever o nome e vencimento dos empregados que ganham mais de 1000 euros, sem necessidade de leitura do ficheiro, mas apenas usando a mesma lista “tab_empregados”, com n elementos. printf("Lista de empregados com mais de 1000 €: \n"); for i = 1:n emp = nth(tab_empregados,i); if emp.venc > 1000 printf("\t%s\t%7.2f\t\n",emp.nome,emp.venc); endif; endfor; 11/12 de Maio de 2004 Estruturas 18 Estruturas e Listas em Funções • Estruturas e listas podem ser retornadas como resultado de uma função. Por exemplo, a leitura de um ficheiro com o formato considerado pode ser feita pela função (que também retorna o número de elementos): function [t, n] = ler_tabela(ficheiro); [f_aux, msg] = fopen(ficheiro, "r"); tab_empregados = list(); n = 0; [emp.cod,emp.nome,emp.venc,emp.data, count] = fscanf(f_aux,"%i%s%f%s","C"); while !feof(f_aux) n = n+1; tab_empregados = append(tab_empregados, emp); [emp.cod,emp.nome,emp.venc,emp.data, count] = fscanf(f_aux,"%i%s%f%s","C"); endwhile; fclose(f_aux); t = tab_empregados; endfunction; 11/12 de Maio de 2004 Estruturas 19