PARTE III - Funções e Procedimentos padrão. Conversão. Personalização gráfica.
Após uma introdução básica e uma parte essencialmente prática e de desenvolvimento da leitura lógica de um programa em Pascal, é altura de continuar com uma nova parte novamente teórica. Os Desafios doravante apresentados pretendem ser exercícios mais complexos que levam o “aprendiz” a pensar mais para poder chegar à conclusão. Nesta parte do Tutorial, dar-se-á ênfase à utilização de operadores para realização de cálculos (revisão prática), recorrendo sempre que possível a procedimentos e funções para que estes sejam treinados e seja entendida a sua importância na estruturação de um bom programa. Esta parte também começará a dar ênfase às boas práticas no acto da programação para a fácil leitura e interpretação de um programa Pascal. Começar-se-á igualmente com optimização de nível básico, e com comandos de coloração e outros direccionados a strings.
1. Operações numéricas gerais
Para além das quatro operações básicas, existem outras suportadas pelo Pascal. Muitas foram já abordadas previamente, mas seguem-se como breve revisão prática.
SIN, COS e ARCTAN
program operacoes_01;
uses crt;
var a : real;
begin
write('Introduza angulo em RADIANOS: ');
readln(a);
write('Seno do angulo: ',SIN(a):0:3); // valor arredondado a 3 casas decimais
write('Co-seno do ângulo: ',COS(a):0:3);
write('Arco-tangente: ',ARCTAN(a):0:3);
readln;
end.
ROUND, TRUNC e ABS
- round – arredonda o número às unidades – útil para converter um Real em Integer;
- trunc – obtém o valor inteiro no número, sem o arredondar – valor truncado;
- abs – devolve o valor absoluto do número.
Exemplo da sua utilização:
program operacoes_02;
var r : real;
begin
write('Introduza numero real: ');
readln(r);
write('Arredondado às unidades: ',ROUND(r));
//equivale a “r:0:0”. Contudo, poderá gravar o valor arredondado numa variável, e “r:0:0” não o faz – tal não é permitido.
write('Parte inteira: ',TRUNC(r));
write('Parte fraccionaria: ',ABS(r-TRUNC(r)));
// considera-se a parte fraccionária sempre POSITIVA, daí a utilização da função ABS.
write('Valor absoluto: ',ABS(r));
write('Absoluto da parte inteira: ',ABS(TRUNC(r)));
readln;
end.
DIV e MOD Estas são operações muito úteis, relacionadas com a divisão. Relembrando a Parte I do tutorial:
- DIV – devolve o resultado de uma divisão, em número inteiro, truncado: só permite a utilização de variáveis Integer;
- MOD – devolve o resto de uma divisão: só permite a utilização de variáveis Integer;
program operacoes_03;
uses crt;
var r, s : real;
begin
write('Introduza dois valores reais: ');
readln(r, s);
write('Divisao “normal”: ',r/s);
// não é prevista a situação de s=0, para simplificação da leitura do programa.
write('Div. Inteira: ',ROUND(r) DIV ROUND(s));
write('Resto: ',ROUND(r) MOD ROUND(s));
readln;
end.
2. Optimização básica
Optimização é o processo que permite reduzir o número de linhas de código: o programa realiza exactamente as mesmas operações com menos código, ou seja, o executável terá um tamanho menor, e poderá ser, porventura, mais rápido de ser processado. As primeiras optimizações que irão ser feitas serão na estrutura de decisão If… Then… Else…. A regra desta estrutura, como já foi visto, é a seguinte:
if (condição) then begin
comandosA;
end
else begin
comandosB;
end;
No caso de comandosA ou comandosB serem uma e uma só instrução, optimiza-se a estrutura das seguintes formas:
// comandosA é uma só instrução:
if (condição) then comandoA
else begin
comandosB;
end;
// comandosB é uma só instrução:
if (condição) then begin
comandosA;
end
else comandoB;
// comandosA e comandosB são uma só instrução:
if (condição) then comandoA
else comandoB;
Por exemplo:
readln(numero);
if (numero<0) then write('Numero negativo.')
else begin
if (numero>0) then write('Numero positivo')
else write('Numero nulo');
end;
Agora, o mesmo se aplica a qualquer ciclo de repetição. No caso de o código a executar forem tão-somente uma instrução, ficará assim:
// ciclo WHILE
while (condição) do comandoA;
// Ciclo FOR
for i:=1 to 10 do comandoB;
for i:=1 downto 10 do comandoC;
Ou seja, em todos os casos, begin e end são as palavras reservadas omitidas. A única estrutura que não tem optimização possível é o ciclo Repeat… Until….
3. Facilitação da leitura de um programa
Imagine-se o seguinte caso. Pretende-se que o utilizador de um programa introduza um número real, em frente à mensagem que lhe colocarmos. Por exemplo, algo como:
write('Introduza um numero real: ');
readln(numero);
Como o cursor fica à frente da mensagem e o número será introduzido em frente deste, o próprio código poderá dar a entender este facto da seguinte forma:
write('Introduza um numero real: '); readln(numero);
// o cursor fica à frente da mensagem
Ou seja, o código em si dá a entender que o programa esperará o valor real (variável numero) à frente da mensagem. Isto é possível pois o ponto e vírgula (“ ; ”) é o símbolo que faz a separação dos códigos. Por esta razão é que, no código que antecede um else nunca é precedido de “;”, pois o programa só assim fica a saber que a condição if ainda não acabou e que encontrará imediatamente a seguir um senão (else).
A própria optimização estudada anteriormente facilita em muito a leitura de um programa, pois há menos “poluição visual” com uma série de begins e ends desnecessários. A leitura torna-se mais clara.
O uso de procedimentos e funções facilitará igualmente a leitura do código, para além de o optimizar. As seguintes duas linhas de código fazem exactamente o mesmo, contudo uma utiliza funções e outra não: (NB: As funções aqui apresentadas estão programadas na Parte II do tutorial.)
// sem recurso a funções:
if (COS(a)<>0) then writeln('A tangente e: ',SIN(a)/COS(a):0:3)
else writeln('Tangente impossivel');
// com recurso a funções:
if tangente_possivel(a) then writeln('A tangente e: ',tan(a):0:3);
else writeln('Tangente impossivel');
Com recurso a funções, consegue-se ler directamente o seguinte:
Se a tangente de a é possível então calcula-a, se não diz que é impossível.
Compare-se com o caso em que não há recurso a funções. A leitura é dificultada. Este é meramente um caso simplista. Casos mais complexos revelarão melhor a importância das funções e dos procedimentos na leitura fácil de um programa, mas tais casos não estão no âmbito deste Tutorial devido à sua complexidade (número de linhas de código envolvidas – o foco desta parte seria desviada para o entendimento do programa em si).
4. A fórmula resolvente – programa completo – uso de funções
Pretende-se um programa que realize a Fórmula Resolvente, recorrendo a Procedimentos e Funções variados. Tópicos para o programa:
- Função para o cálculo do Binómio Discriminante;
- Função para o cálculo de ambos os X (soluções da equação quadrática);
Cumprindo os variados tópicos, deve-se primeiramente definir como funcionará cada Função e cada Procedimento.
program formula_resolvente;
var a, b, c : real;
i : integer;
function resolucao_possivel(d1:real) : boolean; // verifica se há solução real
begin
end;
function d(a1:real; b1:real;c1:real) : real; // calcula o binómio discriminante
begin
end;
function x(i:integer;a1:real;b1:real;c1:real) : real; // calcula o X nº “i”.
begin
end;
begin
writeln('Ax^2 + Bx + C = 0');
writeln('Introduza os tres coeficientes por ordem: ');
readln(a, b, c);
end.
Agora que o esqueleto do programa está montado, resta programá-lo de acordo com as funções de que se dispõe. Para treinar a leitura lógica de um programa que utilize funções, a explicação não será alongada. Claro que o exemplo seguinte tem funções utilizadas com um certo abuso, mas tal foi propositado.
program formula_resolvente;
var a, b, c : real;
i : integer;
function resolucao_possivel(d1:real) : boolean; // verifica se há solução real
begin
if (d1>=0) then resolucao_possivel := true // se o Bin. Disc. For maior ou igual que zero, há soluções reais
else resolucao_possivel := false;
end;
function d(a1:real; b1:real; c1:real) : real; // calcula o binómio discriminante
begin
d:=SQR(b1) – 4*a1*c1;
end;
function x(j:integer; a1:real; b1:real; c1:real) : real; // calcula o X nº “j”.
begin
case j of
1 : x := (-b1 + SQRT( d(a1, b1, c1) )) / (2*a1);
2 : x := (-b1 - SQRT( d(a1, b1, c1) )) / (2*a1);
end;
end;
begin
writeln('Ax^2 + Bx + C = 0');
writeln('Introduza os tres coeficientes por ordem: ');
readln(a, b, c);
if resolucao_possivel( d(a, b, c) ) then begin
// se a resolução é possível (sabendo-se tal pelo Discriminante), então…
for i:=1 to 2 do write('Solucao ',i,': ', x(i, a, b, c):0:3 );
end else begin
write('Impossivel em R.');
end;
readln;
end.
Desafio 1 Crie um programa que leia o nome do utilizador e apresenta-o com os caracteres por ordem inversa. Por exemplo:
“Thoga 31” passará a ser “13 agohT”
Contudo, terá de criar uma função para:
- Ler uma variável string, denominada ler;
- Escrever um texto, denominada escrever;
Normas: livre
5. Conversão de variáveis
Existem, claro está, funções que permitem converter as variáveis de uns tipos para outros. É possível converter de Real para String, de String para Real, de Integer para Real e de Real para Integer. Existem outras variáveis, contudo, que não são abordadas de momento.
5.1. Real/Integer – String
Sejam dadas as variáveis r, Real, e s, String. Sejam igualmente as variáveiserro e i, Integer.
str(s, r, erro);
// Converter a string “s” para real, “r”.
// Se tal não for possível, indicar a posição do erro, em “erro”.
A posição do erro consiste no índice da string no qual existe o erro. Exemplo:
574P5 – o erro está na posição 4: “P”.
str(s, i, erro);
// Converter a string “s” para integer, “i”.
// Se tal não for possível, indicar a posição do erro, em “erro”.
A posição do erro consiste no índice da string no qual existe o erro. Exemplo:
5.915 – o erro está na posição 2: “.”: um Integer não tem parte decimal.
val(r, s);
// Converter o real “r” para string, “s”.
val(i, s);
// Converter o integer “i” para string, “s”.
A conversão de real/integer para string não implicará erro nenhum, caso o processo seja correctamente efectuado na CPU, o que não é previsto o contrário.
5.2. Real – Integer
Considerando as mesmas variáveis:
r := i;
Não são necessárias normas para esta conversão, visto que um número inteiro (Integer) não passa de um caso especial do universo dos reais (Real). Logo são intimamente compatíveis nesta, e só nesta, conversão. Já o inverso não se verifica, pois um número real pode não pertencer ao universo dos inteiros. Para esta conversão, é necessário arredondar o Real, e o Pascal só reconhece esta operação com um de dois operadores: ROUND e TRUNC, cujas diferenças já foram previamente explicadas.
i := ROUND(r);
i := TRUNC(r);
6. Coloração de texto e fundo
O sistema de cores que o pascal suporta é o seguinte:
- Para cor do texto: 4 bits (16 cores);
- Para a cor do fundo: 3 bits (8 cores);
Os códigos que permitem a aplicação de cores são os seguintes:
- Para cor do texto: textcolor;
- Para a cor do fundo: textbackground;
A cor predefinida do texto é a nº 7, e a de fundo é a nº 8.
textcolor(7);
textbackground(8);
Para conhecer as cores do Pascal, pode-se construir o seguinte programa:
program cores;
uses crt;
var i : integer;
begin
writeln('Cores de TEXTO:');
for i:=1 to 16 do begin
textcolor(i);
write(' Cor ',i,' ');
end;
writeln;
writeln('Cores do FUNDO:');
textcolor(15); // branco
for i:=1 to 8 do begin
textbackground(i);
write(' Cor ',i,' ');
end;
writeln;
end.
Desafio 2 Crie um programa que construa uma tabela em que, em cada coluna, fica uma cor de fundo, e em cada linha uma cor de texto, para que possam ser analisadas todas as combinações fundo/texto possíveis. Normas: livre
7. Caracteres ASCII
O sistema de caracteres do Pascal é o ASCII. Dependendo de cada país e/ou sistema, a tabela pode sofrer algumas modificações, as quais o Pascal é sensível pois ele recorre ao sistema ASCII do próprio computador e não a uma tabela ASCII própria. Para se conhecer a tabela ASCII de cada programa, é possível criar um programa que a debite. Contudo, é apresentada uma tabela padrão na Parte VI. Para saber o caracter de ordem i da tabela ASCII:
write( CHAR(i) );
Já para saber a ordem de um caracter:
write( ORD(caracter) );
Sendo caracter uma variável do tipo char.
No caso de string, para se saber a ordem de cada carácter dever-se-á realizar o seguinte ciclo:
for i:=1 to length(s) do write(ORD(s[i]),' ');
(i – variável Integer)
Só com o método CHAR é possível escrever acentuações em determinados IDEs/compiladores.
Desafio 3 Crie um programa que construa uma tabela ASCII, indicando a ordem seguida do carácter correspondente. Normas: livre
Desafio 4 Crie, num programa, tão-somente duas funções que façam uma conversão especial. Muitas vezes, o valor booleano True é associado ao número 1, e False ao número 0. Então, uma das funções converte um booleano no seu correspondente numérico. A outra função realizará o inverso. Normas: livre
Propostas de Exercícios 6. Crie uma calculadora que realize as quatro operações básicas, com previsão de erros, como o da divisão. Estes deverão ser escritos a vermelho vivo. Os textos deverão ser a branco e os resultados a amarelo vivo. Faça um programa em que, no fim, se possa optar se se deseja realizar novo cálculo ou se se deseja sair. Normas:
- Limite de linhas de código (exclusive comentários e linhas em branco): 70 linhas.
7. Um programa deverá desenhar a bandeira da França. Utilize ciclos for. Faça um procedimento para desenhar a bandeira, onde as variáveis de entrada serão a cor a utilizar, o número de colunas e o número de linhas a desenhar. Normas:
- Limite de linhas de código (exclusive comentários e linhas em branco): 30 linhas.