ALGORITMOS COM PASCAL

 

Prof. Miguel Jonathan – Dept. Ciência da Computação – IM/UFRJ – 2003 – versão 1.0

 

CAPíTULO 4

 

Repetição em programas Pascal

 

Desvio incondicional  -  o comando goto

A grande maioria dos programas de computador precisa repetir muitas vezes um mesmo comando, ou grupo de comandos, para realizar a sua tarefa.

Na fase inicial da programação, quando ainda não haviam programas muito grandes, as linguagens de programação dispunham apenas do comando goto (literalmente significando “vá para”) para forçar a repetição de comandos. A ele dá-se o nome de desvio incondicional.

Apesar do seu uso não ser mais recomendado hoje em dia, a compreensão do funcionamento do comando goto ajuda bastante a entender o funcionamento dos modernos comandos de repetição que serão vistos mais adiante.

O comando goto é usado junto com um ponto de retorno chamado de rótulo (label em inglês). Qualquer comando Pascal pode ser precedido por um rótulo seguido de : e o comando goto pode ser usado para forçar um retorno do programa para qualquer um dos rótulos definidos. Rótulos são declarados na seção de declarações do programa, precedidos da palavra-chave label.  Um rótulo pode ser qualquer identificador, ou um inteiro entre 0 e 9999. A sintaxe do comando é:  goto <nome do rótulo>

Os exemplos abaixo ajudam a entender o mecanismo.

Seja obter a soma dos inteiros de 1 até um valor n qualquer maior que 1:

Exemplo 4.1 :

01:program testgoto;

02:var x,soma,n:integer;

03:label 1;

04:begin

05:soma:=0;

06:x:=1;

07:write('Ate'' que numero deseja somar os inteiros? ');

08:readln(n);

09:1: soma:=soma+x;

10:x:= x+1;

11:if x <= n  then goto 1;

12:writeln('A soma de 1 a ', n, ' e'' igual a ', soma);

13:end.

 

No programa acima, as linhas estão numeradas para melhor compreensão das explicações. A seqüência de comandos acima fará com que o programa fique repetindo os comandos das linhas 9, 10 e 11 enquanto o valor de x não ultrapassar o valor de n. Outra maneira de dizer a mesma coisa, é dizer que essas linhas serão repetidas até que o valor de x ultrapasse o valor de n.  Os comandos entre um rótulo e o comando goto para esse rótulo recebem o nome de laço de repetição.

 

O programa seguinte mostra um exemplo mais complexo. Desejamos imprimir a seqüência de inteiros da forma:

122333444455555 etc.

para inteiros variando de 1 até um valor máximo n.

Note que a seqüência tem um laço dentro de outro laço. O laço mais “externo” faz com que os números da seqüência variem de 1 até n, e o laço mais “interno” faz com que, para cada numero k, entre 1 e n, este seja repetido k vezes.

Exemplo 4.2 :

01:program tesgoto2;

02:var x,k,n:integer;

03:label 1,2;

04:begin

05:write('Valor maximo da sequencia(< 20): ');

06:readln(n);

07:k:=0;

08:1: k:= k+1;

09:   x:=1; {x conta quantas vezes k sera' escrito}

10:   2: write(k);

11:   if x<k then

12:    begin

13:    x:= x+1;

14:    goto 2;

15:    end;

16:if k<n then goto 1;

17:end.

 

No programa acima, os comandos das linhas 08 a 16 formam o laço mais externo, e os comandos das linhas 10 a 15 formam o laço mais interno.

 

O comando while

 

O comando while permite eliminar completamente a necessidade de comandos goto explícitos, sem impedir que se possa construir laços de repetição em programas. Com isso, evita-se o uso indiscriminado do comando goto , e ao mesmo tempo o programa ganha mais estrutura. A técnica que evita o comando goto, e utiliza apenas o comando while e equivalentes para laços de repetição, é  chamada de programação estruturad

 

Forma sintática:  while <expressão lógica>  do <comando-a-repetir>

Tradução literal: enquanto <expressão lógica>  faça <comando>

Significado:

“A expressão lógica é calculada. Se o seu valor for True, o comando é executado, e em seguida a expressão volta a ser calculada. Enquanto o valor da expressão for True, o processo se repete: o comando é executado, e a expressão volta a ser calculada. Caso o valor da expressão seja False, o comando não é executado, e o controle passa para a próxima instrução após o while”.

 

O comando:  while <expressão lógica>  do <comando-a-repetir>

é equivalente ao trecho seguinte:

1: if  <expressão lógica> then begin <comando-a-repetir>; goto 1 end;

 

Note que, se a expressao lógica for falsa logo na primeira vez que for calculada, o <comando-a-repetir> não será executado nem uma vez. Obviamente, o <comando-a-repetir> deve conter elementos que façam com que a expressão lógica se torne falsa após um número finito de voltas, caso contrário o laço de repetição nunca terminará. Esse tipo de erro chama-se de laço infinito, e quando ocorre diz-se, no jargão popular de computação, que o programa entrou em “loop”. (“Loop” é a palavra inglesa para laço, e pronuncia-se lup). A única forma de sair desse erro é forçar o fim do programa. No ambiente Turbo Pascal isso é feito pressionando a tecla Control (CTRL), e BREAK, simultaneamente.

 

Exemplo super simples:  O programa abaixo imprime:  12345

 

Exemplo 4.3:

 

program ex4_3;

var i: integer;

begin

 i:=0;

 while i < 5 do

   begin 

    i:= i+1;

    write(i)

   end;

end.

 

Esse programa equivale ao programa abaixo, que usa goto:

 

Exemplo 4.4:

 

program ex4_4;

var

 i: integer;

label 1;

begin

 i:=0;

 1: if i < 5 then

   begin 

    i:= i+1;

    write(i);

    goto 1;

   end;

end.

 

A seguir mostramos como ficam os exemplos 4.1 e 4.2 acima, quando o comando goto é substituído pelo comando while:

 

Exemplo 4.5:

 

program ex4_5;

var x,soma,n:integer;

begin

soma:=0;

x:=1;

write('Ate'' que numero deseja somar os inteiros? ');

readln(n);

while x <= n do

 begin

 soma:=soma+x;

 x:= x+1;

 end;

writeln('A soma de 1 a ', n, ' e'' igual a ', soma);

end.

 

Exemplo 4.6:

 

program ex4_6;

(******************************************

 Este programa imprime a seqüência

 122333444455555... nnnn,  de 1 a n.

 Utiliza dois laços de repetição while,

 um dentro do outro.

******************************************)

var x,k,n:integer;

begin

write('Valor maximo da sequencia(< 20): ');

readln(n);

k:=0;

while k<n do

 begin

 k:= k+1;

 write(k);

 x:=1; {x conta quantas vezes k sera' escrito}

 while x<k do

  begin

   write(k);

   x:= x+1;

  end; {fim do while interno}

 end;  {fim do while externo}

end.

 

 

A linguagem Pascal ainda dispõe de dois outros comandos para construir laços de repetição:

 

O comando repeat

 

Forma sintática: repeat <seqüência de comandos a repetir> until  <expressão lógica>

Tradução literal:  repita <seqüência de comandos a repetir> até  <expressão lógica>

Significado:

“A seqüência de comandos é executada. A seguir, a expressão lógica é calculada. Se o seu valor for False, a seqüência de comandos volta a ser executada e, em seguida, a expressão é novamente calculada. O processo se repete até que valor da expressão se torne True. Nesse momento, o comando repeat termina, e o  controle passa para o próximo comando do programa.”

 

A diferença principal de significado entre o comando while e o comando repeat é que, no comando repeat, a seqüência de comandos a repetir é executada sempre pelo menos uma vez, enquanto que no comando while o comando a repetir pode não ser nunca executado.

 

Há outras diferenças mais sutis, somente na forma:

a)      No comando while o que é repetido é “um comando”, enquanto que no comando repeat o que é repetido é  “uma seqüência de comandos”.

A diferença é apenas na forma de escrever (sintática): se você quiser repetir uma seqüência de comandos com while, basta colocá-las dentro de um bloco begin ....end, e com o comando repeat, não é necessário envolver a seqüência em um bloco. O motivo é que as palavras repeat e until já são suficientes para indicar ao compilador o inicio e o fim dos comandos a repetir.

b)      No comando while, o teste para saber se o comando terminou é verificar se a expressão lógica se torna false. No comando repeat, o teste para saber se o comando terminou é quando a expressão se torna true (verdadeira). O motivo é que o significado do comando while é : enquanto a expressão for verdadeira, faça algo, e o do comando repeat é faça algo até que a expressão se torne verdadeira.

 

O exemplo abaixo mostra como fica o programa do exemplo 4.3 quando o while é substituído por um repeat:

 

Exemplo 4.7:

 

program ex4_7;

(************************

 uso de repeat..until

************************)

var i: integer;

begin

 i:=0;

 repeat

  i:= i+1;

  write(i)

 until i>=5

end.

 

Note as diferenças em relação ao Exemplo 4.3:

-         a expressão lógica i>=5 é o oposto da expressão anteior i<5

-         não foi necessário envolver os comandos a repetir em um bloco begin-end.

 

 

O comando for

 

O comado for é uma forma abreviada de se escrever o comando while, para alguns casos particulares mais freqüentes. A descrição abaixo se refere ao caso em que a variável de controle é inteira, mas esse comando pode ser usado com qualquer variável de controle que seja ordinal, ou seja, cujos elementos possuam uma ordem determinada, como é o caso dos caracteres e outros tipos enumerados ainda não vistos.

 

Formas sintáticas:

    for <variável de controle> :=  <expressão inicial inteira> to  <expressão final inteira> do <comando a repetir>

Significado:

    “a expressão inicial é calculada e a expressão final é calculada (ambas inteiras) com os valores na memória no momento em que o comando for inicia. A variável de controle recebe o valor inicial. Se esse valor for menor ou igual que o valor final, então o comando a repetir é executado, a variável de controle é incrementada de 1 unidade, e o novo valor da variável é comparado com o valor final. Enquanto o valor da variável de controle for menor ou igual ao valor final, o comando a repetir será novamente executado, e a variável novamente incrementada. O comando termina quando o valor da variável de controle ficar maior que o valor final.”

 

IMPORTANTE: a expressão inicial e final são calculadas apenas uma vez, no inicio da execução do comando for. Mesmo que valores de variáveis que façam parte da expressão final forem alterados pelo comando a repetir, isso não altera o valor final que será usado nos testes de comparação.

 

A segunda forma sintática, ligeiramente diferente, é a seguinte:

 

    for <variável de controle>  :=  <expressão inicial inteira> downto  <expressão final inteira> do <comando a repetir>

 

Significado:

    “a expressão inicial é calculada e a expressão final é calculada (ambas inteiras) com os valores na memória no momento em que o comando for inicia. A variável de controle recebe o valor inicial. Se esse valor for maior ou igual que o valor final, então o comando a repetir é executado, a variável de controle é decrementada de 1 unidade, e o novo valor da variável é comparado com o valor final. Enquanto o valor da variável de controle for maior ou igual ao valor final, o comando a repetir será novamente executado, e a variável novamente decrementada. O comando termina quando o valor da variável de controle ficar menor que o valor final.”

 

A diferença é que a variável de controle é incrementada no primeiro caso, e decrementada no segundo caso.

 

Exemplo 4.8:

 

Programa para escrever a seqüência:  123....n  para um n inteiro qualquer.

 

program ex4_8;

(**************************

 uso de for incrementando

 a variável de controle

**************************)

var i,n: integer;

begin

 write ('Digite um valor de n inteiro: ');

 readln(n);

 for i:= 1 to n do write(i);

end.

 

Compare com o programa 4.3 ou 4.7. Não é necessário incrementar a variavel i explicitamente, pois isso é feito automaticamente pelo comando for. E também não é preciso fazer o teste se i  ainda é menor que n, pois isso também é feito automaticamente. Com isso o programa fica simplificado. A forma ideal de “ler” o programa acima é: “para i variando de 1

até 5, imprima o valor de i”.

 

O exemplo seguinte escreve a seqüência invertida, na forma:  n n-1 .... 4321, para um n inteiro qualquer:

 

Exemplo 4.9:

 

program ex4_9;

(**************************

 uso de for decrementando

 a variável de controle

**************************)

var i,n: integer;

begin

 write ('Digite um valor de n inteiro: ');

 readln(n);

 for i:= n downto 1 do write(i);

end.

 

 

 

O próximo exemplo imprime em uma única tela todos os caracteres da tabela.

 

Nota: em Pascal existem duas funções especiais pré-definidas, que servem para fazer a associação entre os caracteres e suas posições na tabela de caracteres. São elas:

ord (c)   onde c é uma expressão que representa um caractere. A função retorna um inteiro entre 0 e 255, que é a posição (ou número de ordem) do caractere na tabela. Ex: ord (‘A’) é 65.

 

chr(n)  onde n é uma expressão inteira com valor entre 0 e 255. A função retorna o caractere que fica na posição n da tabela. Por ex:  chr(65)  é ‘A’.

 

Seja agora escrever um programa para listar todos os 256 elementos da tabela em uma única tela de saída. A tela tem 24 linhas, com 80 caracteres em cada linha.

 

Podemos ver toda a tabela em uma única tela se tivermos 11 colunas, com 24 caracteres em cada uma (11 x 24 = 264). Para as 11 colunas caberem em uma linha, cada coluna deve ter 7 caracteres. A saída terá a aparência seguinte (apenas alguns caracteres são mostrados aqui, os demais estão representados por um -):

coluna  j®

  0     1      2      3      4      5      6      7     8      9      10 

linha i

¯

  0 -   24 -   48 0   72 H   96 -  120 x  144 -  168 -  192 -  216 -  240 -

  1 -   25 -   49 1   73 I   97 a  121 y  145 -  169 -  193 -  217 -  241 -

  2 -   26 -   50 2   74 J   98 b  122 z  146 -  170 -  194 -  218 -  242 -

..........................................................................

..........................................................................

 23 -   47 -   71 G   95 -  119 w  143 -  167 -  191 -  215 -  239 -        

 

O programa deve ter um laço de repetição para escrever as 24 linhas e, para cada linha, outro laço para escrever as 11 colunas.

 

Vamos usar a variável i para numerar as linhas de 0 a 23, e a variável j para numerar as colunas, de 0 a 10. Uma análise do quadro acima mostra que a posição do caractere k está na linha i e coluna j que satisfazem a relação:   k = j* 24 + i .

 

O programa deve levar ainda em consideração que alguns caracteres não representam símbolos a serem escritos. Eles são usados como sinais de controle para o dispositivo de saída (monitor de vídeo ou impressora) e portanto não convém que se tente imprimi-los. No lugar deles, colocamos duas letras que indicam a sua função. São eles:

 

 8  backspace, faz o cursor voltar uma posição para a esquerda (BS)

10  line feed, faz o cursor descer para a linha seguinte. É representado por LF

13  carriage return, ou retorno do carro, faz o cursor voltar para a posição inicial da linha (CR)

 

O caractere da posição 0 também não tem representação impressa, ele é o caractere nulo, na tabela representado por NUL. O caractere 7 também não tem representação impressa, e tem o nome de BEL.

O programa abaixo cria então a tabela desejada. Note que em cada coluna, o valor de k é escrito com 3 posições,  seguido do caractere chr(k), ocupando 2 posições, e seguido de mais 2 espaços para completar as 7 posições da coluna. Para os caracteres especiais que não tem representação impressa, a expressão chr(k) é substituída por uma palavra adequada, ou por espaços:

 

program tabela;

uses crt;

var i,j,k: integer;

begin

clrscr;

 for i:= 0 to 23 do begin

  for j:= 0 to 10 do begin

    k:= j* 24 + i;

    if k<=255 then begin

     write(k:3);

     case k of

     0: write(' NUL');

     7: write(' BEL');

     8: write(' BS ');

     10: write(' LF ');

     13: write (' CR ');

     else write(chr(k):2, '  ');

     end; {case}

    end; {if}

  end; {for interno}

  writeln;

 end; {for externo}

end.