|
MC102 |
| 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!