Curso de Pascal
Vetores e Matrizes
Você está em: MarMSX >> Cursos >> Pascal
As variáveis como os integer, string, char, real, etc são capazes de armazenar apenas um dado de cada vez para uma determinada entidade (nome, idade, nota, cargo etc). Entretanto, se desejarmos armazenar mais de um dado para a mesma entidade, como fazer?
Para os programadores iniciantes, o programa mais intuitivo para armazenar o nome de 10 pessoas na memória é o seguinte:
var nome1, nome2, nome3, nome4, nome5, nome6, nome7, nome8, nome9, nome10 : string[40];
begin
write('Entre o nome da 1a. pessoa: ');
readln(nome1);
write('Entre o nome da 2a. pessoa: ');
readln(nome2);
write('Entre o nome da 3a. pessoa: ');
readln(nome3);
write('Entre o nome da 4a. pessoa: ');
readln(nome4);
write('Entre o nome da 5a. pessoa: ');
readln(nome5);
write('Entre o nome da 6a. pessoa: ');
readln(nome6);
write('Entre o nome da 7a. pessoa: ');
readln(nome7);
write('Entre o nome da 8a. pessoa: ');
readln(nome8);
write('Entre o nome da 9a. pessoa: ');
readln(nome9);
write('Entre o nome da 10a. pessoa: ');
readln(nome10);
end.
O programa ficou longo e utiliza diversas variáveis. Observe que para acessar cada nome, temos que referenciar a respectiva variável. Imagine se desejássemos utilizar 1000 nomes!
Podemos corrigir esse inconveniente e criar um programa mais simples e elegante, através do uso de vetores.
Vetores
O vetor é uma lista de variáveis do mesmo tipo, com um tamanho fixo e disposta sequencialmente na memória do computador.
A sintaxe de um vetor é a seguinte:
var nome : array[inicio..fim] of tipo_de_dado
A declaração de um vetor é semelhante à declaração da variável. A diferença está na uso da palavra reservada "array", que indica a criação de um vetor, mais os limites dele.
Diferente do Basic e do C, onde indicamos o tamanho do vetor, no Pascal informamos o menor valor do índice (inicio), seguido do maior valor de índice (fim).
Exemplo de criação de um vetor de valores inteiros:
var nota : array[1..10] of integer;
É uma lista de valores inteiros, variando de 1 até 10.
Para acessar um elemento do vetor, deve-se utilizar o nome do vetor, seguido do número da posição que desejamos ler/alterar os dados entre colchetes. Exemplo:
nota[4] := 8.0; { Escreve na posição 4 do vetor }
writeln(nota[4]); { Lê e imprime o valor contido no índice 4 do vetor }
Agora, vamos reescrever o programa dos nomes, criando um vetor do tipo string[40] para 10 nomes.
var nome : array[1..10] of string[40];
i : integer;
begin
for i:=1 to 10 do
begin
write('Entre com o nome da ',i,'a. pessoa: ');
readln(nome[i]);
end;
end.
Observe como o programa ficou mais fácil de entender e mais simples.
O identificador array indica que queremos criar um vetor (lista), de tamanho igual a dez, numerado de 1 até (assinalado com '..') 10, e do tipo string[40].
Referência a um vetor
Conforme visto, a referência a uma posição da lista é sempre feita através do nome da variável de vetor, seguida de colchetes contendo o índice do vetor.
Podemos também fazer referência diretamente ao vetor, e ter acesso total a ele. Para isso, utilizamos apenas o nome do vetor, sem os colchetes.
Veja o exemplo a seguir e os comentários entre as chaves.
var vet1, vet2 : array[1..5] of integer;
begin
vet1[1] := 7; { Acessa item do vetor, na posição 1, alterando o valor para 7 }
vet2 := vet1; { Copia todo o vetor 1 para o vetor 2 }
end.
Aprofundando: disposição do vetor na memória
Um vetor nada mais é do que um agrupamento de dados do mesmo tipo, com um tamanho pré-definido. Dessa maneira, os dados estão dispostos na memória de forma contígua (em seqüência).
De forma a ilustrar como os dados são armazenados na memória, vamos criar um vetor de números inteiros de tamanho igual a 5.
var vet : array[1..5] of integer;
O vetor não é inicializado. Assim, se não colocarmos os valores em cada posição, teremos os valores que estiverem na memória (lixo) no momento de execução. Esses valores são aleatórios.
Vamos atribuir um valor para cada posição do vetor "vet":
var vet : array[1..5] of integer;
begin
vet[1] := 7;
vet[2] := 5;
vet[3] := 9;
vet[4] := 8;
vet[5] := 3;
end.
Um variável do tipo inteiro ocupa 2 bytes na memória. Dessa forma, os dados do vetor "vet" estarão dispostos na memória da seguinte maneira:
| 1 | 2 | 3 | 4 | 5 | - Posição do vetor
|-------+-------+-------+-------+-------+
| 07 00 | 05 00 | 09 00 | 08 00 | 03 00 | - Dado na memória (valores em hexadecimal)
Quando acessamos a posição 3 do vetor (assinalado em azul a seguir), o valor 9 é retornado.
| 1 | 2 | 3 | 4 | 5 | - Posição do vetor
|-------+-------+-------+-------+-------+
| 07 00 | 05 00 | 09 00 | 08 00 | 03 00 | - Dado na memória (valores em hexadecimal)
Exemplos de vetores de outros tipos:
Char:
var vet : array[1..5] of char;
| 1 | 2 | 3 | 4 | 5 | - Posição do vetor
|----+----+----+----+----+
| 75 | 82 | 28 | 10 | 49 | - Dado na memória (valores em hexadecimal)
Real:
var vet : array[1..2] of real;
| 1 | 2 | - Posição do vetor
|-------------------+-------------------+
| 01 02 CD AB 10 12 | 45 55 65 75 AD A0 | - Dado na memória (valores em hexadecimal)
Matrizes
Podemos também criar matrizes, isto é, um vetor com n dimensões.
Uma tabela é um exemplo de uma matriz de duas dimensões e um cubo é um exemplo de uma matriz de três dimensões.
Para criar uma matriz, usamos também a palavra reservada "array". Para vetores multidimensionais, a sintaxe é:
var nome : array[variacao_dim_1, variacao_dim_2, ..., variacao_dim_n] of tipo_de_dado;
Para matrizes de duas dimensões (tabela), a linha representa a dimensão 1, enquanto que a coluna representa a dimensão 2.
var matriz : array[variacao_linha, variacao_coluna] of tipo_de_dado;
No exemplo a seguir, será criada uma tabela (matriz) que guarda o dia, o mês e o ano de nascimento de 10 pessoas. A tabela terá 10 linhas e 3 colunas.
var data : array[1..10, 1..3] of integer;
A tabela equivalente é ilustrada a seguir.
|
DIA |
MES |
ANO |
PESSOA 1 |
|
|
|
PESSOA 2 |
|
|
|
PESSOA 3 |
|
|
|
PESSOA 4 |
|
|
|
PESSOA 5 |
|
|
|
PESSOA 6 |
|
|
|
PESSOA 7 |
|
|
|
PESSOA 8 |
|
|
|
PESSOA 9 |
|
|
|
PESSOA 10 |
|
|
|
Obs: A área assinalada em amerelo é a informação armazenada na memória do computador. A área em verde é apenas o cabeçalho da tabela para a compreensão e localização dos dados.
Acesso aos dados na matriz
O acesso aos dados da matriz é feito utilizando-se o índice correspondente a cada dimensão criada, separados por vírgula e respeitando a ordem de criação.
No exemplo da tabela anterior, fazemos o acesso ao índice da linha, depois ao da coluna:
data[linha, coluna];
Por exemplo, para imprimir o dia de nascimento da "pessoa5", fazemos:
writeln(data[5,1]);
O programa a seguir cadastra a data de nascimento de 10 pessoas na tabela criada.
var data : array[1..10, 1..3] of integer;
i : integer;
begin
for i:=1 to 10 do
begin
writeln('Pessoa numero ',i);
write('Dia: ');
read(data[i,1]);
write('Mes: ');
read(data[i,2]);
write('Ano: ');
read(data[i,3]);
end;
end.
Inicialização de vetores e matrizes no Pascal
Apesar dos compiladores de Pascal atuais aceitarem a inicialização de variáveis do tipo vetor, no Turbo Pascal isso somente é possível quando o vetor é criado como constante.
A regra de atribuição é a seguinte:
Vetor:
(posição_1, posição_2, ..., posição_n);
Matriz NxM:
(
(linha_1_Coluna_1, ..., linha_1_Coluna_M),
...,
(linha_N_Coluna_1, ..., linha_N_Coluna_M)
);
Exemplos:
const
vetor : array[1..5] of integer = (1, 3, 5, 7, 9);
var
i : integer;
begin
for i:=1 to 5 do
write(vetor[i], ' ');
end.
Saída:
1 3 5 7 9
const
vetor : array[1..3,1..2] of integer = ((1, 3), (5, 7), (9,11));
var
i, j : integer;
begin
for i:=1 to 3 do
begin
for j:=1 to 2 do
write(vetor[i,j], ' ');
writeln;
end;
end.
Saída:
1 3
5 7
9 11
Os parêntesis mais internos correspondem às dimensões mais à direita do vetor. Exemplo com um cubo:
type cube = array[0..1,0..1,0..1] of integer;
const maze : cube = (((0,1),(2,3)),((4,5),(6,7)));
var i,j,k : integer;
begin
for i:=0 to 1 do
begin
for j:=0 to 1 do
begin
for k:=0 to 1 do
writeln(maze[i,j,k]);
end;
end;
end.
Adaptado de: Turbo Pascal reference manual, 1983.
Algumas considerações
Só podemos criar listas ou tabelas com o mesmo tipo de variável. Entretanto, é possível definir tipos de variáveis compostas (ver capítulo de tipos) e utilizá-las em vetores. Definindo um variável composta, seria possível termos um registro com o nome do tipo String, a idade do tipo inteiro e a altura do tipo real, e armazená-las em um vetor.
O tamanho da tabela é fixo. Deve ser definido antes de rodar o programa, em sua construção.
Podemos criar matrizes com N dimensões. Basta seguir a regra da matriz. Exemplo para 3 dimensões:
var cubo : array[1..20, 1..3, 1..50] of integer;
...
begin
cubo[1,2,5] := 15;
end.
Não é obrigatório o uso completo de um vetor ou matriz. Por exemplo, podemos criar uma tabela com 1000 linhas e usar somente 200. Entretanto, não podemos criar uma tabela com 200 linhas e querer utilizar 1000. Assim, o tamanho da matriz deverá ser dimensionado para o uso esperado, de forma que não haja desperdício ou falta de espaço.
Listas de tamanho variável
Já sabemos que o vetor não permite o redimensionamento de seu tamanho. Entretanto, é possível criar listas com o tamanho variável. Para isso, devemos criar um vetor com o tamanho máximo previsto para essa lista e utilizar somente a quantidade de campos necessários.
Principais características:
- Uma variável irá controlar a quantidade de elementos presentes na lista.
- A parte da lista utilizada vai de 0 a quantidade-1.
- Os elementos deverão estar dispostos de forma contígua. Assim, não deverão haver espaços em branco.
- A inserção e remoção de elementos deverá deslocar os demais elementos da lista.
Inserção
Há dois modos de inserção: o primeiro insere sempre ao final da lista, não sendo necessário o deslocamento dos elementos, enquanto que o segundo insere o elemento em uma posição P da lista, sendo necessário o deslocamento dos outros elementos, uma vez que não podemos sobrescrever sobre uma informação que já ocupa a posição P.
Exemplo de inserção do valor "7" ao final da lista:
tamanho = 3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
Insere:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 7 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
tamanho = 4
Exemplo de inserção do valor "7" na posição "1" da lista:
tamanho = 3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
Desloca (abre espaço):
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
Insere:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 7 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
tamanho = 4
O deslocamento DEVERÁ sempre começar pelo último elemento. Se fosse começado pelo primeiro elemento a ser deslocado, ele sobrescreveria o próximo elemento.
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
Procedimento correto:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
Procedimento Errado:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 5 │ 5 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
---->
Remoção
Há dois modos de remoção: o primeiro remove sempre ao final da lista, não sendo necessário o deslocamento dos elementos, enquanto que o segundo remove o elemento em uma posição P da lista, sendo necessário o deslocamento dos outros elementos.
Exemplo de remoção do último elemento da lista:
tamanho = 4
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 7 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
Remove:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │ 7 │xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
tamanho = 3
Exemplo de remoção do item da posição "1" da lista:
tamanho = 3
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 5 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
Desloca:
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 4 │ 9 │ 9 │xxx│xxx│xxx│xxx│xxx│xxx│xxx│
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
0 1 2 3 4 5 6 7 8 9
↑
tamanho = 2
Em ambos os casos, o "lixo" permanece na antiga última posição. Entretanto, o tamanho da lista diminuiu e esse "lixo" é ignorado.
Diferente da inserção, o deslocamento agora deverá começar pelo primeiro elemento a ser deslocado.
Programa completo
O programa a seguir demonstra o uso de uma lista com tamanho variável.
const v : array[1..10] of byte = ( 4,5,9,0,0,0,0,0,0,0 );
var total : byte;
procedure push(valor : byte);
begin
if (total>= 10) then
exit;
total := succ(total);
v[total] := valor;
end;
procedure insere(valor, pos : byte);
var i : byte;
begin
if ((total>= 10) or (pos < 1) or (pos >= total)) then
exit;
for i:=total downto pos do
v[i+1] := v[i];
v[pos] := valor;
total := succ(total);
end;
procedure pop;
begin
if (total<=0) then
exit;
total := pred(total);
end;
procedure remove(pos : byte);
var i : byte;
begin
if ((total<= 0) or (pos < 1) or (pos > total)) then
exit;
for i:=pos+1 to total do
v[i-1] := v[i];
total := pred(total);
end;
procedure imprime;
var i : byte;
begin
for i:=1 to total do
write(v[i], ' ');
writeln;
end;
begin
total := 3;
imprime;
push(7);
imprime;
insere(6,2);
imprime;
pop;
imprime;
remove(1);
imprime;
end.
Saída:
4 5 9
4 5 9 7
4 6 5 9 7
4 6 5 9
6 5 9