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.
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.
Pode-se criar um conjunto, nomeado (com nome), de uma certa natureza. Teoricamente, há que declarar o conjunto pela seguinte ordem:
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.
Ao operar com conjuntos, estamos a falar de três operações básicas, familiares da Teoria de Conjuntos na Matemática:
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]
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.
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.
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:
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:
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:
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.
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.
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.
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.
Lista das características básicas:
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.