PARTE V - Conjuntos. Estruturação de um programa em Pascal. Data e Hora do Sistema.
Nesta parte são abordados os Conjuntos, que são uma estrutura constituível pelo programador, isto é, uma colecção, de elevada potencialidade em Pascal. É abordada mais a fundo uma forma simples de estruturar um programa recorrendo à estrutura Begin/End. E, por fim, é dado, por curiosidade, três procedimentos padrão de uma nova biblioteca que permitem determinar a data e a hora do sistema, e obrigar o programa a parar durante um certo tempo antes de avançar.
1. Conjuntos
Parte deste capítulo foi baseado em: GOTTFRIED, Byron S., Programação em Pascal, McGraw-Hill, 1994 Algumas frases poderão ser iguais às do livro. Parte dos exemplos é retirado igualmente deste livro. Isto deve-se ao facto de este livro estar muito bem conseguido no que toca a Conjuntos, tornando-se inigualavelmente claro.
Um conjunto é uma colecção de elementos simples ordenados, todos do mesmo tipo. Um conjunto não é fácil de se declarar, mas, anteriormente, já os utilizámos de forma inconsciente, aquando a escrita, por exemplo, de:
while not A in [0..9] do
[0..9] é um conjunto de reais, que começa em 0 (zero), inclusive, e termina em 9 (nove), inclusive. Já no programa que analisava as avaliações dos alunos, utilizámos a seguinte notação:
case avaliacao of
0..3 : writeln('Fraco');
// etc.
end;
0..3 representa o intervalo de números reais entre zero e três, inclusive.
1.1. Declaração e utilização
Pode-se criar um conjunto, nomeado (com nome), de uma certa natureza. Teoricamente, há que declarar o conjunto pela seguinte ordem:
- Declarar os elementos base como Type;
- Declarar o conjunto em termos de elementos base, como Type;
- Declarar o nome de conjunto, como Var, sendo do tipo conjunto.
Parece um processo complicado, e com passos sem sentido. Contudo, esta organização faz parte da estruturação do Pascal como linguagem estruturada – pode-se mesmo dizer que “as coisas não aparecem do nada nem por milagre”. Veja-se a declaração do conjunto em termos teóricos:
type elementos_base = (dado1, dado2, …, dado n);
conjunto = set of elementos_base;
var nome_de_conjunto : conjunto;
Agora aplique-se a teoria num exemplo prático:
type medidas = (pequeno, medio, grande);
medidas_camisa = set of medidas;
var manga : medidas_camisa;
Pode-se definir um conjunto com dados padrão, tal como, por exemplo:
type algarismos = set of 0..9;
minusculas = set of 'a'..'z';
Contudo, um conjunto pode ser construído. Imagine-se uma calculadora, tal como já foi dada como exercício anteriormente, onde o utilizador pode escolher o operador. Podía-se declarar um conjunto dos operadores, e, no código, simplificar a escrita do programa:
program calculadora;
uses crt;
type conjunto_operadores = set of char;
var operadores : conjunto_operadores;
oper : char;
begin
oper := ['+', '-', '*', '/'];
// …
repeat
oper := readkey;
until char(oper) in operadores;
// …
end.
Como se constata, oper foi declarado como uma variável. Contudo, podia ser declarado como uma constante, não sendo necessário declarar dois tipos.
program calculadora;
uses crt;
const operadores = ['+', '-', '*', '/'];
var oper : char;
begin
// …
repeat
oper := readkey;
until char(oper) in operadores;
// …
end.
O primeiro exemplo é um excelente exemplar da declaração de um conjunto através de tipos, e o segundo exemplo mostra a criação de um conjunto, constante, através de um conjunto individual de elementos ordenados e definidos, inalteráveis. A utilização deste conjunto simplifica na medida em que, na estrutura Repeat… Until…, torna-se mais legível, ao invés de:
repeat
oper := readkey;
until (char(oper)='+') or (char(oper)='-') or (char(oper)='*') or (char(oper)='/');
Os conjuntos são úteis na medida em que é criada uma colecção de elementos ordenados, irrepetíveis, como sendo um grupo. Para quem é conhecedor de VB, pode-se fazer a analogia óbvia de que uma colecção em Pascal é o mesmo que uma enumeração (Enum). Por exemplo, a colecção de operadores criada anteriormente em Pascal seria, em VB, algo como:
Private Enum operadores As Char
"+"
"-"
"*"
"/"
End Enum
Seguem-se pequenos exemplos de subconjuntos, aplicando o exemplo das medidas:
type medidas = (pequeno, medio, grande);
medidas_camisa = set of medidas;
var manga_curta, manga_comprida, outra_manga : medidas_camisa;
begin
// …
manga_curta := [pequeno, grande];
manga_comprida := [pequeno..grande];
outra_manga := [grande];
// …
end.
Não é fácil visualizar utilizações “muito” práticas dos conjuntos, em Pascal. Contudo, será construído, mais à frente, um programa para exemplificar a sua utilidade. Para já, vamos aprender a realizar operações com conjuntos.
1.2. Operações com conjuntos
Ao operar com conjuntos, estamos a falar de três operações básicas, familiares da Teoria de Conjuntos na Matemática:
- Intersecção;
- União;
- Diferença.
Será abordada cada uma delas, por ordem, através de exemplos práticos, todos seguindo a colecção criada anteriormente sobre medidas de camisas.
INTERSECÇÃO A intersecção baseia-se no princípio de obter os elementos que estão em comum entre dois ou mais conjuntos. Por exemplo, a intersecção entre o conjunto de números {1, 2, 3, 4, 5} e o conjunto {0, 3, 4, 6} é o conjunto {3, 4}. Na matemática, esta operação representa-se por uma multiplicação. Assim o é em Pascal:
manga_curta := [pequeno, medio] * [medio, grande];
Neste caso, manga-curta será, por fim, o conjunto [medio]. Já no seguinte caso:
manga_comprida := [pequeno] * [grande];
manga_comprida será o conjunto vazio, [ ]. Como é constatável, existe o conjunto vazio em Pascal. Ora, isto obriga desde já a retirar uma propriedade da intersecção.
A intersecção entre um conjunto e o conjunto vazio é o próprio conjunto vazio.
UNIÃO (ou REUNIÃO) A união, ou reunião, de dois ou mais conjuntos resulta num novo conjunto que contenha todos os elementos, sem excepção, de todos os conjuntos envolvidos na operação, sem que haja elementos repetido. Por exemplo, a reunião entre os conjuntos {1, 2, 3, 4} e {0, 3, 4, 6} é o conjunto {0, 1, 2, 3, 4, 6}, e não o conjunto {0, 1, 2, 3, 3, 4, 4, 6}. Regra geral, na matemática esta operação representa-se como sendo uma “soma”. Em Pascal:
manga_curta := [pequeno] + [grande];
Neste caso, resulta o conjunto [pequeno, grande].
manga_comprida := [pequeno, grande] + [medio, grande];
Agora, resulta o conjunto [pequeno, medio, grande]. Mais, uma propriedade da união é intrínseca na análise desta operação:
A união entre um conjunto e o conjunto vazio é o próprio primeiro conjunto.
DIFERENÇA Pegando em dois conjuntos, a diferença do primeiro pelo segundo resulta num conjunto constituído pelos elementos do primeiro excepto os do segundo. Por exemplo, {1, 2, 3, 4} – {0, 3, 4, 6} dará o conjunto {1, 2}. Já no caso da operação {0, 3, 4, 6} – {1, 2, 3, 4}, o resultado será o conjunto {0, 6}. A diferença de conjuntos é feita, na matemática, exactamente pela operação da subtracção. Em pascal, o mesmo acontece:
manga_curta := [pequeno, medio] – [pequeno, grande];
Neste caso, o conjunto solução é [medio].
manga_comprida := [pequeno, medio, grande] – [medio];
Já neste caso, o conjunto solução é [pequeno, grande]. Duas propriedades, intimamente relacionadas, podem ser deduzidas:
A diferença de um conjunto pelo conjunto vazio é o próprio primeiro conjunto.
A diferença do conjunto vazio por outro conjunto é o próprio conjunto vazio.
Segue-se um exemplo de atribuição de conjuntos, com operações:
manga_curta := [pequeno, medio, grande];
manga_comprida := manga_curta – [pequeno];
// manga_comprida = [medio, grande]
manga_curta := manga_curta * (manga_comprida – [grande]);
// manga_curta = [medio]
1.3. Comparação de conjuntos
Após as operações de conjuntos, segue-se então a comparação. Em matemática, isto resume-se em expressões como “pertence a”, “está contido em” ou “não está contido em”. O Pascal tem a capacidade de realizar estas comparações, tendo, para tal, quatro operadores. O resultado resultante destas comparações é sempre e tão-somente um valor booleano.
- = – Igualdade – ambos os conjuntos contêm os mesmos membros, independentemente da ordem;
- <> – Desigualdade – os conjuntos não contêm exactamente os mesmos membros;
- <= – Inclusão – cada membro do primeiro conjunto está contido no segundo conjunto;
- >= – Inclusão – cada membro do segundo conjunto está contido no primeiro conjunto.
Analise-se, então, uma série de comparações entre conjuntos, e o resultado retornado por cada uma:
Operação de comparação | Resultado lógico |
---|---|
[pequeno, grande] = [pequeno, medio, grande] | falso |
[pequeno, grande] = [grande, pequeno] | verdadeiro |
[pequeno, médio, grande] <> [pequeno..grande] | falso |
[pequeno, grande] <> [grande] | verdadeiro |
[pequeno] <= [pequeno..grande] | verdadeiro |
[pequeno..grande] <= [grande] | falso |
[] <= [pequeno, grande] | verdadeiro |
[pequeno..grande] >= [pequeno, medio] | verdadeiro |
[grande] >= [pequeno, grande] | falso |
De novo, conclui-se uma nova propriedade dos conjuntos:
O conjunto vazio está sempre contido em qualquer outro conjunto.
1.4. Análise de uma linha de texto
O programa que se segue é um programa completo, explicado através de comentários ao longo deste. Isto serve para treinar a leitura lógico-formal de um programa em Pascal.
program letras_usadas;
(* Programa que determina quais as letras utilizadas numa linha de texto *)
uses crt;
type cartas = set of char;
var usada, naousada : cartas; // letras usadas e não usadas
contador, caractcontador : 0..80; // contadores
alfa : char;
linha : string[80]; // linha de texto, com um máximo de 80 caracteres
procedure ler;
(* Lê uma linha de texto *)
begin
for contador := 1 to 80 do linha[contador] := ' '; // “limpa” a linha de texto
writeln('Introduza uma linha de texto');
readln(linha);
caractcontador := length(linha);
{para o contador de caracteres vai o comprimento da string LINHA.}
end;
procedure mostrar;
(* Mostra a análise realizada *)
begin
writeln;
write('Letras utilizadas:');
for alfa := 'A' to 'z' do if [alfa] <= usada then write(' ', alfa);
{Se o caracter ALPHA está contido no conjunto de letras usadas, então escreve-se esse carácter,
uma vez que é uma letra usada na linha de texto.}
writeln;
writeln;
end;
begin (* Bloco principal *)
ler; // lê a primeira linha de texto
while not (upcase(linha) = 'FIM') do begin // analisa a linha de texto introduzida
{termina o programa quando de tiver escrito “FIM”.}
usada := []; // define, no início, que nenhuma letra está utilizada
naousada := ['A'..'Z', 'a'..'z']; // define, no incício, que todas as letras não foram utilizadas
for contador := 1 to caractcontador do begin // analisa a linha de texto, caracter a caracter.
if [linha[contador]] <= naousada then begin
{se o conjunto definido pelo caracter nº CONTADOR da linha de texto
está contido no conjunto das letras não usada, então…}
usada += [linha[contador]]; // essa tal letra passa a pertencer ao conjunto das letras usadas…
naousada -= [linha[contador]]; // e deixa de pertencer às não usadas.
end;
end;
mostrar; // mostra o resultado
ler; // volta a ler nova linha de texto
end;
end.
Propostas de Exercícios 12. Crie um programa que analise uma linha de texto e diga ao utilizador quantas e quais as vogais, bem como as consoantes, utilizadas nessa mesma linha, de forma separada. Normas:
- Devem ser utilizados conjuntos;
- Dever-se-á recorrer a um ou mais Record para organizar os vários dados necessários;
- Limite de linhas de código (exclusive comentários e linhas em branco): livre.
Desafio 6 Existe um método de determinar os números primos de 2 até n conhecido como Crivo de Eratosthenes. O método é o seguinte:
- Gerar uma lista ordenada de números inteiros, de 2 até n;
- Para um dado número i, da lista, fazê-lo sair da lista e introduzi-lo na de números primos, e, na lista original, eliminar todos os múltiplos de i
- Repetir o passo número 2, para cada número sucessivo de i, começando em i=2.
Escreva um programa que utilize este método para determinar os números primos de 2 até n, sendo n um valor dado pelo utilizador. Recorra a conjuntos para resolver este problema. Normas:
- Limite de linhas de código (exclusive comentários e linhas em branco): livre.
2. Estruturação de um programa em Pascal
Muitas vezes, e regra geral, as bases da estruturação de um programa em pascal são dadas no início. Contudo, e pessoalmente falando, eu coloco este método em questão, e deixei-o mais para o fim. Para quê aprender princípios em estruturação se mal se conhece a linguagem e mal se sabe programar em Pascal? Pode-se abordar tal antes de se aprender a linguagem, mas não vai passar de pura teoria. É necessária uma componente prática, penso. Agora, após as grandes bases sobre Pascal, chega a altura de serem dadas umas noções básicas sobre a boa estruturação de um programa nesta linguagem de programação.
2.1. Estrutura Begin… End
Um procedimento, uma função, uma estrutura de decisão, uma estrutura de repetição e o bloco principal de um programa são todos delimitados pelas palavras Begin e End. Contudo, é possível criar partes de um programa, estruturando-o em blocos individuais, utilizando estas palavras reservadas. Segue-se um exemplo muito simples.
program hello_world;
begin (* BLOCO PRINCIPAL *)
begin (* Bloco de Escrita *)
writeln('Hello World!');
end;
begin (* Bloco de Pausa *)
readln;
end;
end.
Ou seja, podem ser criados blocos com uma ou mais linhas de código delimitados por estas duas palavras reservadas. Estas palavras reservadas não têm de ser obrigatoriamente usadas tão-somente nas situações referidas no início deste subcapítulo.
2.2. A cláusula Exit
Este é um procedimento padrão do Pascal, que permite que o programa saia de um bloco delimitado por Begin e End, sem que tenha de executar as restantes linhas de código. Isto não se aplica à estrutura Begin… End que delimita estruturas de decisão e de repetição, apesar de existirem raros compiladores que aceitem esta cláusula dentro destas estruturas – regra geral, aquando um Exit dentro de uma destas estruturas, e que esta esteja no bloco principal, o programa encerra:
program hello_world;
var i : integer;
begin (* BLOCO PRINCIPAL *)
// …
for i := 1 to 10 do begin
if i = 4 then exit;
writeln('Hello World!');
end;
// …
end.
Devido ao facto de a estrutura de repetição For estar dentro do Bloco Principal, regra geral, a cláusula Exit obriga o programa a sair do bloco principal, ou seja, encerra-o. Contudo, se este ciclo se encontrasse dentro de um “sub-bloco”, então o programa apenas sairia do ciclo For e continuava no Bloco Principal:
program hello_world;
var i : integer;
begin (* BLOCO PRINCIPAL *)
// …
begin
for i := 1 to 10 do begin
if i = 4 then exit;
writeln('Hello World!');
end;
end;
writeln('Continuando…');
// …
end.
Neste caso, quando i=4, o programa “saltaria” para fora do ciclo For e recomeçava na linha de código writeln('Continuando…');. Veja-se, então, um exemplo muito simples da utilização da cláusula Exit:
program hello_world;
uses crt;
var i:integer;
procedure escrever;
(* Bloco de Escrita *)
begin
if i>=3 then exit;
writeln('Hello World!');
end;
begin (* BLOCO PRINCIPAL *)
for i:=1 to 5 do escrever;
readln;
end.
O output gerado por este programa é tão-somente o seguinte:
Hello World!
Hello World!
Isto porque, quando i >= 3, é “executada” a cláusula Exit, que obriga o programa a “saltar” do procedimento escrever, não executando a linha de código seguinte, que é a que faz escrever o texto.
2.3. Etiquetas
Assim que surgiu a programação, surgiu a ideia básica de redireccionamento do programa para certos pontos deste, para que o programa não fosse tão-somente linear, mas sim dinâmico. Existem linguagens de programação, hoje em dia em muito arcaicas, como FORTRAN ou BASIC, que utilizam abusivamente deste princípio. Normalmente, em qualquer linguagem de programação, o programa é redireccionado com a palavra reservada GOTO, obrigando o programa a dirigir-se a um ponto nomeado (com um nome). O Pascal também suporta este paradigma. Contudo, devido ao facto de possuir estruturas de repetição, estruturas estas que antigamente não existiam no mundo da programação, a utilização de etiquetas torna-se desaconselhada quando a intenção é recorrer a elas de forma abusiva. Contudo, pode contribuir para parte da boa organização de um programa se utilizada de forma pouca, simples e muito racional. Por exemplo, pode-se criar, num programa que necessite de Menu, uma Label (i.e., etiqueta) denominada menu, e que evite que TODO o programa esteja dentro de uma estrutura Repeat… Until ou While…. Pegando nesta situação, vamos exemplificar a declaração de uma Label, o seu “posicionamento” no programa e a forma de se lhe aceder a certa altura.
program exemplo;
uses crt;
label menu;
begin (* bloco principal *)
// …
menu: // ponto do programa denominado “menu”: é a etiqueta.
// …
goto menu;
// …
end.
Este é considerado o método universal de declaração e utilização das etiquetas, pois, regra geral, funciona em qualquer compilador. Há muitos anos atrás houve breves versões do Pascal que nem sequer possuía a palavra reservada label, pois bastava colocar a meio do programa, por exemplo, Menu:. Esta forma de declarar etiquetas cumpre melhor o princípio de “linguagem estruturada” que o Pascal segue, pois tudo é declarado no início, incluindo as etiquetas, para que o computador já esteja “preparado” para todas as situações “que derem e vierem”, falando segundo a gíria popular portuguesa.
NÃO utilize as etiquetas abusivamente. Dão péssimos hábitos de programação quando utilizadas de tal forma.
2.4. Características desejáveis num programa
Lista das características básicas:
- Integridade – precisão dos cálculos;
- Clareza – facilidade da leitura do programa (código-fonte);
- Simplicidade – o programa deve-se focar no seu objectivo;
- Eficiência – Velocidade de execução o mais reduzida possível, com resultados exactos, e correcta utilização da memória;
- Modularidade – programas complexos podem ser decompostos em módulos, sendo eles procedimentos, funções ou simples blocos Begin… End. Deve-se evitar o uso de Labels pois podem criar maus vícios de programação, embora sejam igualmente um método.
3. Data e hora do sistema
Antes de dar por terminada esta Parte sobre a linguagem de programação Pascal, é pertinente terminar com uma ferramenta que muitos gostam de utilizar para personalizar os seus programas – mostrar qual a data e a hora do sistema onde o programa é executado. Para tal, é necessário declarar variáveis do tipo Word e utilizar a bibliotecas dos, pois, como o nome da biblioteca indica, a obtenção destes dados implica o acesso ao sistema do computador, que não é mais do que uma função que o DOS realiza constantemente.
program relogio_sistema;
uses crt, dos;
var tempo : record
relogio : record
hora : word;
minuto : word;
segundo : word;
centesimo_segundo : word;
end;
data : record
ano : word;
mes : word;
dia : word;
dia_semana : word;
end;
end;
begin
with tempo do begin
with relogio do begin
gettime(hora, minuto, segundo, centesimo_segundo);
// determina a hora do sistema
end;
with data do begin
getdate(ano, mes, dia, dia_semana);
// determina a data do sistema
end;
end;
// …
end.
Este exemplo explica muito bem por si só como funcionam estes comandos, devido à legibilidade das variáveis utilizadas, e organizadas em Record.
Outra ferramenta a saber é a Delay, que obriga o programa a esperar um determinado tempo, dito em milissegundos, antes de avançar. Vejamos:
// …
write('Pausa de 5 segundos… ');
delay(5000);
// …
Pode ser útil para a construção de um cronómetro muito simples, ou para evitar que o programa encerre imediatamente assim que lhe é ordenado tal, podendo obrigar a janela ficar em “branco” durante, por exemplo, meio segundo – torna o encerrar do programa mais suave e, possivelmente, agradável.