MC102
Algoritmos e Programação de Computadores
Parte XI



Norton Trevisan Roman




O COMANDO WHILE

Lembra nosso programa de chute? Nós dávamos 10 chances ao usuário e, então saíamos. O que acontece se quiséssemos dar infinitas chances? Não poderíamos usar o FOR. Iríamos querer algo assim:

  enquanto o usuário não acertar
    peça novo chute
	

Como fica isso em Pascal?

  PROGRAM chute;
  CONST num = 5;
  VAR nao_acertou : boolean;
      chute       : integer;

  BEGIN
    nao_acertou := true;

    WHILE nao_acertou DO
    BEGIN
      write('seu chute: ');
      readln(chute);
      IF chute = num THEN BEGIN
        writeln('acertou!');
	nao_acertou := false
      END
      ELSE
        IF chute > num THEN writeln('é >')
	ELSE writeln('é <')
    END {while}
  END.
	

Então, em termos gerais:

  WHILE condição DO comando;
	

A condição é como no IF, podendo ter AND, OR e NOT

Como o WHILE funciona? Se condição for verdadeira, ele executa o comando (ou grupo de comandos). Aí, ao final, ele testa novamente a condição e, se ela continuar verdadeira, executa comando novamente. Faz isso até que condição seja falsa.

Ou seja, faz exatamente o que diz: "Enquanto condição faça comando".

Um lembrete: sempre inicialize as variáveis da condição.

Se a condição for falsa antes do WHILE, ele nem será executado:

  continua := false;
  WHILE continua DO write('nunca será executado');
	

Com isso vemos que não precisamos mais do coxambre horrível do FOR. Aliás, um FOR pode ser feito com WHILE, veja abaixo:

	                      cont := 1;
FOR cont:=1 TO N DO           WHILE cont<=N DO
  comando;                    BEGIN
                                comando;
                                cont := cont + 1
		              END;
	

Use sempre WHILE quandovocê precisar sair do laço a qualquer momento, como no exemplo do programa onde o usuário adivinhava o número.

Considere o programa abaixo:

  n := 1;
  WHILE n <> 10 DO BEGIN
    write(n);
    n := n+2
  END;
	

Quando esse programa pára? Nunca! Pois n nunca é 10. Ele imprime os ímpares ad infinitum. Se quiséssemos imprimir os 5 primeiros ímpares deveríamos fazer:

  n := 1;
  WHILE n < 10 DO BEGIN
    write(n);
    n := n+2
  END;
	

Nunca use <> quando < ou > bastam.

Considere agora esse fragmento:

  x := 5;
  WHILE x > 0 DO BEGIN
    x := x - 1;
    write(10/x)
  END;
	

Qual o erro deste? O erro é que antes subtraímos, daí escrevemos, para depois testar. Assim, quando x=1, faço x:=x-1 => x=0 e divido 10 por 0 => erro!

Como conserto isso? O modo mais direto seria:

  x := 5;
  WHILE x > 0 DO BEGIN
    x := x - 1;
    IF x>0 THEN write(10/x)
  END;
	

Mas isso não parece legal, pois estamos fazendo duas vezes o mesmo teste. Tem como melhorar? Tem:

  x := 4;
  WHILE x > 0 DO BEGIN
    write(10/x);
    x := x - 1
  END;
	

Agora garanto que ao decrementar x, testo se ele é maior que zero.

Assim, é bom seguir essa receita com laços WHILE:

  gero ou leio o primeiro valor
  WHILE há outros valores
    processo o valor
    gero ou leio o próximo
	

Dessa forma garantimos que só valores testados são processados.

Como foi visto no IF, a condição pode ser qualquer expressão, variável ou valor booleano. Assim, o seguinte programa nunca pára:

  WHILE true DO writeln('não pára!');
	

pois a condição é sempre verdadeira. Além disso, operações como AND, OR e NOT são permitidas.

Em suma: o laço WHILE testa a condição e, se esta for verdadeira, então executa seu corpo (o que está entre BEGIN e END, ou o comando que segui o DO, snão não houver BEGIN e END). Após executar o corpo, ele testa novamente a condição, executando novamente o corpo se esta for verdadeira. Assim ele seguie até que a condição se torne falsa, quando ele "pula" o corpo e o programa passa ao comando seguinte ao WHILE.

Como exemplo, façamos um programa que tire a média de n notas do usuário (n desconhecido). Se o usuário entrar -1 ele sai do programa:

  PROGRAM media;
  VAR soma, nota : real;
      cont       : integer;

  BEGIN
    soma := 0;
    cont := 0;
    nota := 0;
    WHILE nota <> -1 DO BEGIN
      write('nota: ');
      readln(nota);
      soma := soma + nota;
      cont := cont + 1
    END;
    writeln('A média é ',soma/cont)
  END.
	

Que erros temos aí? Dois grandes! O primeiro é que, quando o usuário entra com -1, ele é somado. Então temos que deixar o read para o final (para deposi da soma):

  PROGRAM media;
  VAR soma, nota : real;
      cont       : integer;

  BEGIN
    soma := 0;
    cont := -1; {se 0 conta um a mais, verifique}
    nota := 0;
    WHILE nota <> -1 DO BEGIN
      soma := soma + nota;
      cont := cont + 1;
      write('nota: ');
      readln(nota)
    END;
    writeln('A média é ',soma/cont)
  END.
	

Mas ainda dá para melhorar! Notou que da primeira vez, soma=0, nota=0 e faço soma := soma+nota? Posso evitar essa soma desnecessária assim:

  PROGRAM media;
  VAR soma, nota : real;
      cont       : integer;

  BEGIN
    soma := 0;
    cont := 0;
    nota := 0;
    write('nota: ');
    readln(nota)
    WHILE nota <> -1 DO BEGIN
      soma := soma + nota;
      cont := cont + 1;
      write('nota: ');
      readln(nota)
    END;
    writeln('A média é ',soma/cont)
  END.
	

Agora sim... o -1 não entra na soma nem faço somas desnecessárias.

E qual era o segundo erro? E se da primeira vez o usuário puser de cara um -1? Aí soma=0, cont=0 e soma/cont=0/0 = erro! Então temos que por um teste no final:

  PROGRAM media;
  VAR soma, nota : real;
      cont       : integer;

  BEGIN
    soma := 0;
    cont := 0;
    nota := 0;
    write('nota: ');
    readln(nota)
    WHILE nota <> -1 DO BEGIN
      soma := soma + nota;
      cont := cont + 1;
      write('nota: ');
      readln(nota)
    END;
    IF cont>0 THEN writeln('A média é ',soma/cont)
  END.
	

Pronto!





‹— Parte XPágina da disciplinaParte XII —›