MC102 |
LAÇOS ANINHADOS |
Suponha que quiséssemos desenhar um quadrado 10 X 10 de *. Como faríamos?
Faça 10 vezes: escreva uma linha com 10 * |
Naturalmente, um programa que use esse algoritmo fucniona:
FOR cont:=1 TO 10 DO writeln('**********'); |
Mas, e se quisermos deixar o tamanho do quadrado como variável?
FOR cont:=1 TO l DO escreva l vezes um * |
Como escrevemos l vezes um *? Da mesma forma que fizemos os l conjuntos de l *:
faça l vezes: escreva um * |
Ou seja:
FOR cont:=1 TO l DO write('*'); |
Então, para nosso quadrado:
FOR cont:=1 TO l DO BEGIN FOR cont2 := 1 to l do write('*'); writeln; END; |
Rode o programa e veja sua saída. Será um quadrado com o lado que você determinar em l.
Reparou que as duas variáveis contadoras são diferentes? Elas tem que ser diferentes, senão, ao mudarmos o valor de uma, estaremos mudando também o valor da que controla o outro for (pois são a mesma). Por exemplo, veja a execução do seguinte programa, observe o valor das variáveis:
l := 3; FOR cont:=1 TO l DO BEGIN FOR cont := 1 to l do write('*'); writeln END; cont 1 do primeiro FOR 1 do segundo FOR 2 do segundo FOR 3 do segundo FOR saída: *** |
Por que parou? Porque cont já chegou a l no laço interno (de fato, l é 4), então o laço externo entende que já fez as 3 vezes dele também. Não confunda! Não use a mesma variável!
Mas, e se tivermos 2 laços disjuntos? Aí podemos usar a mesma variável. Quer um exemplo? Vamos desenhar 2 quadrados l X l, um ao lado do outro:
FOR cont:=1 TO l DO BEGIN FOR cont2 := 1 TO l DO write('*'); write(' '); FOR cont2 := 1 TO l DO write('*'); writeln END; |
Então posso usar a mesma variável, mas somente para laços disjuntos. No caso acima, a coisa funciona porque quando o segundo FOR interno muda o valor de cont2, o primeiro laço não precisava mais desse valor. Mas se um FOR estiver dentro do outro, então eles não são disjuntos, e não posso usar a mesma variável para ambos os contadors.
E se, agora, quisermos escrever n quadrados de lado l, um ao lado do outro?
FOR cont:=1 TO l DO BEGIN FOR cont2 := 1 TO n DO BEGIN FOR cont3 := 1 TO l DO write('*'); write(' ') END; writeln END; saída (n := 3, l := 3): *** *** *** *** *** *** *** *** *** |
ITERAÇÕES ANINHADAS |
Vejamos outro exemplo: desenhe o seguinte triângulo:
...*... ..***.. .*****. ******* |
Primeiro, vamos observar que esse é um retângulo de pontos e asteriscos de tamanho l X (2l-1), onde l é o número de linhas e (2l-1) o de colunas. Então temos uma relação entre as dimensões do retângulo.
Note também que:
para cada linha l, até n linhas faça: desenhe n-l '.' desenhe 2l-1 '*' desenhe n-l '.' |
E como fazemos isso em pascal?
FOR l:=1 TO n DO BEGIN FOR cont := 1 TO (n-l) DO write('.'); FOR cont := 1 TO (2*l-1) DO write('*'); FOR cont := 1 TO (n-l) DO write('.'); writeln END; |
Reparou que usei cont para todos? Mais do que isso, notou como os limites dos FOR internos depende do contador do FOR externo? Ou seja, tome o primeiro FOR interno, seu limite é (n-l), ou seja, a cada iteração do FOR externo, o valor de l aumenta, aumentando esse limite. Isso é uma iteração aninhada.
Com isso vemos uma característica do FOR: o ponto de início e o de fim de um laço pode ser qualquer expressão de resultado inteiro.
Note também que quando l valer n, o primeiro e o terceiro FOR interno não serão executados, pois (n-l) será 0, e o final do laço é menor que o início.
DOWNTO |
Como vimos, o comando FOR incrementa seu contador de forma crescente. Mas, e se quiséssemos decrementar o contador, ou seja, subtrair 1 dele a cada laço, fazendo uma contagem regressiva? Para tal usamos downto:
FOR cont:=10 DOWNTO l DO writeln(cont); saída: 10 9 8 ... 3 2 1 |
Simples não? Vejamos um exemplo, vamos gerar dois triângulos, um em cima do outro, assim:
..*.. .***. ***** ***** .***. ..*.. |
Como fazemos isso? Usamos o seguinte algoritmo:
escrevemos um triângulo normal escrevemos um triângulo de cabeça para baixo |
A primeira parte já vimos como fazer, mas e a segunda? Como desenhamos um triângulo de cabeça para baixo? Vamos primeiro ver como desenhávamos um triângulo normal:
escrevíamos a primeira linha escrevíamos a segunda linha ... escrevíamos a n-ésima linha |
E como fazemos com o invertido?
escrevemos a n-ésima linha escrevemos a (n-1)-ésima linha ... escrevemos a primeira linha |
Ou seja:
FOR l:=n DOWNTO 1 DO BEGIN FOR cont := 1 TO (n-l) DO write('.'); FOR cont := 1 to (2*l-1) DO write('*'); FOR cont := 1 to (n-l) DO write('.'); writeln END; |
Se antes o FOR não era executado se contador > limite, agora não será se contador < limite. Por exemplo, o seguinte for:
FOR cont:=1 DOWNTO 2 DO write('não vai escrever'); |
não é executado nenhuma vez.