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!