Ir para o conteúdo

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.