Ir para o conteúdo

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.