Segue-se agora para uma parte do Pascal mais complexa e que começa a mostrar novas potencialidades desta linguagem de programação. Trabalhar-se-á com variáveis do tipo Record, extremamente importante em Pascal, e dar-se-ão as bases sobre as variáveis Text, variáveis que permitem criar ficheiros de texto simples. Serão dados de igual forma códigos úteis sobre strings e sobre o teclado e o cursor.
Estas variáveis são extremamente úteis para, por exemplo, programas de registos. Uma variável Record tem várias “subvariáveis”, designadas por campos. Eis um exemplo da declaração de uma variável Record:
var registo : record nome : string; idade : integer; sexo : char; end;
Para pedir ao utilizador, por exemplo, para registar a idade, fá-lo-á do seguinte modo:
write('Registe a idade: '); readln(registo.idade);
Ou seja, a variável Record faz-nos lembrar um formulário onde devemos preencher os vários campos, por exemplo num CV. A variável Record é um formulário, e as suas várias “subvariáveis” (campos) são os campos desse formulário. É possível criar uma “resma” de formulários sob a forma de um array de Record – isto revela que é possível criar milhares de campos em memória, organizados por grupos individuais, de uma forma muito simples e eficaz, sem recorrer a milhares de variáveis. Ou seja, em vez de termos as variáveis nome1, nome2, nome3, idade1, idade2, idade3, sexo1, sexo2 e sexo3, temos três registos (Record), cada um com os seus campos idade, nome e sexo. Em código:
type tipo_reg = record nome : string; idade : integer; sexo : char; end; var registo : array [1..3] of tipo_reg;
Então, pode-se preencher os 3 formulários (array [1..3]) recorrendo a um ciclo for.
for i:=1 to 3 do begin writeln('A FAZER REGISTO NUMERO ',i); write('Nome: '); readln(registo[i].nome); repeat write('Sexo (M/F): '); readln(registo[i].sexo); //só aceita os caracteres “M” e “F”: until (upcase(registo[i].sexo) = 'M') or (upcase(registo[i].sexo) = 'F'); repeat write('Idade: '); readln(registo[i].idade); // a idade é positiva ou nula: until (registo[i].idade >= 0); writeln('REGISTO ',i,' DE 3 EFECTUADO.'); writeln; end;
Da mesma forma, estes registos podem ser escritos com um ciclo for:
for i:=1 to 3 do begin writeln('A VER REGISTO NUMERO ',i); writeln('Nome: ',upcase(registo[i].nome)); writeln('Sexo: ',upcase(registo[i].sexo)); writeln('Idade: ',registo[i].idade); writeln; end;
Assim acabou por ser criado um programa completo que realiza um registo básico de 3 pessoas, escrevendo o que foi introduzido no final. Este fica, então, como um programa exemplo sobre este tipo de variáveis, essenciais em Pascal:
program variavel_record; uses crt; type tipo_reg = record nome : string; idade : integer; sexo : char; end; var registo : array [1..3] of tipo_reg; i : integer; begin for i:=1 to 3 do begin // REGISTO writeln('A FAZER REGISTO NUMERO ',i); write('Nome: '); readln(registo[i].nome); // registo do NOME repeat // registo controlado do SEXO write('Sexo (M/F): '); readln(registo[i].sexo); until (upcase(registo[i].sexo) = 'M') or (upcase(registo[i].sexo) = 'F'); repeat // registo controlado da IDADE write('Idade: '); readln(registo[i].idade); until (registo[i].idade >= 0); writeln('REGISTO ',i,' DE 3 EFECTUADO.'); writeln; end; writeln; writeln; for i:=1 to 3 do begin // ESCRITA writeln('A VER REGISTO NUMERO ',i); writeln('Nome: ',upcase(registo[i].nome)); writeln('Sexo: ',upcase(registo[i].sexo)); writeln('Idade: ',registo[i].idade); writeln; end; readln; // pausa até ENTER end.
Propostas de Exercícios 8. Crie um programa que registe os dados sobre uma turma inteira e faça a estatística básica. Serão pedidos o número de alunos, e o nome e as avaliações a Matemática e a Português de cada um. No final, o programa faz a estatística e diz quais os alunos aptos a ir a Exame Nacional (média igual ou superior a 9,5) de cada disciplina, qual a média da turma a Matemática e a média a Português. Normas:
Existe um código, também denominado, neste caso, de cláusula, que permite “pegar” num Record e trabalhar com ele, sem que se tenha de escrever constantemente o nome do registo. Por exemplo:
with registo[2] do begin nome := 'Thoga31'; idade := 0; sexo := 'M'; end;
Substitui:
registo[2].nome := 'Thoga31'; registo[2].idade := 0; registo[2].sexo := 'M';
Ou seja, simplifica muito a escrita de código, e permite maior legibilidade.
Até ao momento, os programas têm sido direccionados para as consolas DOS. Como se sabe, a consola DOS tem sempre o cursor a piscar, numa determinada posição. Em Pascal é possível controlar esta posição através de um comando extremamente simples. Considere-se a janela como um gráfico cartesiano, só de valores inteiros. Seja X a abcissa (horizontal) e Y a ordenada (vertical). A posição inicial do cursor na consola é no canto superior esquerdo: X=1 e Y=1. Ande-se duas linhas para baixo: X=1 e Y=3. Ande-se com o cursor nove colunas para a frente (direita): X=10 e Y=3. Se quisermos posicionar o cursor nesta última posição:
gotoxy(10, 3);
gotoxy(x, y) Este código só é utilizável quando declarada a biblioteca crt. Caso contrário, é um código não declarado.
É possível gravar a qualquer altura qual a tecla premida por um utilizador. Esta função é extremamente útil na criação de menus apelativos. O utilizador em vez de escrever, por exemplo, “1” e, de seguida, premir ENTER, bastar-lhe-á premir a tecla “1” e, se for uma opção válida do menu apresentado, avança imediatamente, sem ser necessário premir ENTER. Por exemplo:
uses crt; // biblioteca OBRIGATÓRIA var tecla : char; // (. . .) writeln('MENU'); writeln('1 > Repetir operacao'); writeln('2 > Sair'); repeat tecla := readkey; // grava na variável TECLA qual a tecla (key) premida pelo utilizador until (char(tecla) = '1') or (char(tecla) = '2');
O código readkey determina a tecla premida pelo utilizador, gravando-a numa variável char. Este código só é utilizável quando declarada a biblioteca crt. Caso contrário, é um código não declarado.
Propostas de Exercícios 9. Crie uma calculadora que grave num Record, em campos distintos, os dois termos da operação, e o operador. O utilizador deverá escolher o operador sem ter de premir ENTER, segundo uma lista dos operadores disponíveis (soma, subtracção, multiplicação e divisão). Preveja situações de erro, e permita que o utilizador as corrija. O utilizador, com um novo menu, sem recurso ao ENTER, irá escolher se deseja sair do programa ou realizar novo cálculo. Utilize coloração de texto variada, e não de fundo. O ecrã deverá ter fundo cinza claro. Normas:
O Pascal tem a capacidade de trabalhar com ficheiros. Existem duas variáveis para o fazer, contudo vamos abordar apenas aquela que permite escrever e ler ficheiros de texto simples, ou seja, ficheiros cujo conteúdo não passa de texto simples, não formatado: é a variável Text, cujo nome denuncia o tipo de ficheiros que vai trabalhar. O funcionamento desta variável é em tudo diferente das variáveis com que já trabalhámos até agora.
Vamos, então, estudar esta variável por partes e com exemplos práticos. O primeiro programa apresentado cria um ficheiro TXT e grava nele o texto “Hello World! For Pascal TEXT!”. O ficheiro é nomeado de texto.txt.
program escrever_ficheiro; var ficheiro : text; begin assign(ficheiro, 'texto.txt'); // “assina” em memória o ficheiro de texto simples, e cria-o junto do executável rewrite(ficheiro); // ordena a re-escrita do ficheiro – caso o ficheiro já exista e contenha texto, este será eliminado write(ficheiro, 'Hello World! For Pascal TEXT!'); // escreve no ficheiro o texto close(ficheiro); // “encerra” o ficheiro em memória end.
Quando se tenta aceder a um ficheiro de texto simples que esteja “assinado” em memória por um programa deste género, aparecerá a famosa Message Box a dizer que o ficheiro está a ser utilizado por outro processo. Isto ajuda a explicar o que acontece no sistema aquando o código assign.
Agora, vamos criar um novo programa, que ficará no mesmo directório, e que vai abrir o ficheiro anterior e contar o número de espaços do texto que contém:
program ler_ficheiro; var ficheiro : text; s : string; contador, i : integer; begin assign(ficheiro, 'texto.txt'); reset(ficheiro); // “reinicia” o ficheiro, ou seja, acede-lhe mas não pode re-escrever dados nele, só pode ler readln(ficheiro, s); contador := 0; for i:=1 to length(s) do if s[i] = ' ' then contador += 1; // a linha de código anterior recorre à Optimização writeln('Existem ',contador,' espacos no ficheiro.'); close(ficheiro); readln; end.
Desafio 5 Investigue sobre o CV (Curriculum Vitae) europeu – o Europass. Crie um programa que crie um CV deste modelo, cumprindo todos os campos, excepto o da Fotografia. O programa irá registar os dados do utilizador e, de seguida, irá mostrar por passos o que o utilizador introduziu. No caso de confirmação, deverá gravar o CV num ficheiro TXT cujo nome será “CV_nome”, onde “nome” é o nome do utilizador, sem espaços. Os campos não preenchidos não deverão aparecer no ficheiro. Caso o utilizador pretenda alterar um campo, tal deverá ser permitido sem que, para tal, seja necessário refazer o CV todo. Normas:
Seja a variável s uma String com o seguinte texto:
BEM VINDO
pos('M', s);
Determina a posição do primeiro “M” que encontrar: neste caso, 3. Devolve um valor Integer, a ser gravado numa variável deste tipo.
delete(s, 7, 3);
Elimina 3 caracteres a partir da posição 7, inclusive. A string passa a ser “BEM VI”.
insert('- ', s, 4);
Insere, na posição 4, o texto “- ”. A string passa a ser: “BEM – VINDO”.
Propostas de Exercícios 10. Crie um programa em que o utilizador introduza uma string e possa realizar testes variados com ela. A original deverá manter-se intacta, sendo uma string auxiliar a utilizada para as operações. O utilizador poderá escolher a posição, os caracteres e textos, entre outros, relacionados com os códigos anteriores. Normas: livre.
Uma das capacidade do computador é o cálculo de números aleatórios. Estes números não são propriamente aleatórios - chama-se a este processo pseudo-aleatoriedade, isto porque o computador não tem a capacidade de "pensar" num número aleatoriamente. Existem diversos algoritmos, praticamente todos baseados no tempo. Este princípio baseia-se, geralmente, num Timer do computador em que, quando é requerido um número pseudo-aleatório, este calcula-o segundo uma fórmula que inclui os limites mínimo e máximo do conjunto no qual deve estar o número, e o Timer. É devolvido então o resultado de um cálculo, e não um número totalmente aleatório. Em Pascal, é necessário, na generalidade dos compiladores, "activar" este Timer para que possam ser debitados valores pseudo-aleatórios.
randomize;
Não existe nenhum procedimento que "pare" o Timer. Contudo, esta última instrução pode ser constantemente utilizada sem "efeitos". O Pascal só admite conjuntos de 0 a maxint (i.e., o número máximo inteiro positivo suportado), inclusive. Para requerer um número pseudo-aleatório que pertença a um conjunto de 0 a n, inclusive:
random(n);
Esta função devolve um número real. Crie-se, então, um pequeno programa que debite em toda a consola um código binário aleatório:
program binario; uses crt; var i, j : integer; begin randomize; for i:=1 to 20 do begin for j:=1 to 80 do begin gotoxy(j,i); write(TRUNC(RANDOM(2))); end; end; readln; end.
É utilizada a função Trunc devido ao facto de Random devolver números tais como, por exemplo, 0.365, 1.845 (aqui abreviados). Assim, apenas é mostrado o valor inteiro do número, não arredondado - ou 0 ou 1, tal como desejado.
Propostas de Exercícios 11. Simule a Lei dos Grandes Números num programa que irá "lançar" um dado cúbico regular um milhão (1.000.000) de vezes. O programa só deverá gerar como output o número de vezes que "saiu" cada face. Tenha em conta que não existe a face 0 (zero), isto por causa do "defeito" do comando Random - neste caso, o computador deverá calcular nova face, sem que conte como um lançamento. Normas: livre.
A utilização de Object Pascal (nomeadamente com Delphi) veio trazer inovações à programação em Pascal, e uma dessas inovações foi a introdução de arrays dinâmicas.
Estas arrays são declaradas como qualquer outra array, mas sem especificarmos a sua dimensão (fazemos isso mais tarde com SetLength
):
program dynarrays1; var a: array of integer; begin setlength(a, 2); a[0] := 1; a[1] := 2; writeln('Soma: ', a[0] + a[1]); end.
Como podem reparar, as arrays dinâmicas têm índice de base 0; não é possível alterar isto.
Uma nota sobre a utilização destas arrays: sempre que chamamos SetLength
o programa tem que alocar a memória correspondente ao segundo parâmetro multiplicado pelo tamanho (em bytes) do tipo de dados armazenado na array, e seguidamente copiar para lá a array antiga.
Isto torna-se extremamente ineficiente se fizermos muitos redimensionamentos das arrays, e por isso esta opção dinâmica deve ser utilizada com consciência dos seus defeitos.
No caso das arrays dinâmicas multidimensionais, a sua declaração e utilização faz-se da seguinte forma:
program dynarrays2; var a: array of array of integer; begin // equivalente a array [0..9, 0..1] of integer setlength(a, 10, 2); // array [0..1] of array of integer (uma array fixa de arrays dinâmicas) setlength(a, 2); // podemos depois especificar o tamanho de cada uma das "subarrays" setlength(a[0], 4); setlength(a[1], 3); // ... end.