Introdução à programação
com Python em um contexto visual


Laço de repetição while

Veja aqui motivos para usar laços com while, a sintaxe, a descrição de estratégias de uso, em abstrato, e, em seguida, alguns exemplos mais concretos.

Quando é comum usar o while?

A sintaxe dos laços com while

De forma geral os laços de repetição baseados no while tem a seguinte estrutura:

while «condição»: #  enquanto a condição for verdadeira, execute:
    «corpo» 

Em contraste com estrutura do if, em que quando a condição é avaliada como True (verdadeiro) o bloco de código do corpo é executado uma única vez, No caso do while, o bloco do corpo será executado diversas vezes, enquanto a condição for verdadeira, podendo inclusive resultar em um loop infinito. Para evitar essa repetição infinita é necessário garantir que, no código do corpo, em algum momento:

Uma dessas duas coisas é necessária para evitar uma repetição infinita.

Vejamos uma descrição em abstrato de algumas dessas estratégias (que podem inclusive ser combinadas):

«é criada uma variável-contador» 
while «condição que depende do contador»:
    «corpo do laço, deve incluir 
    atualização ou incremento do contador»     
«é criada uma estrutura de dados» 
while «condição que envolve a estrutura de dados»:
    «corpo do laço, deve incluir
    modificação da estrutura de dados»      
while True: # um laço inicialmente infinito
    «corpo do laço, deve incluir
    efeito que afeta a condição de saída»
    if «condição de saída»:
        break # saída do laço    

Somando larguras variáveis

Supanha que queremos acumular retângulos de larguras aleatórias até uma determinada largura total máxima. No corpo do while() há um mecanismo que checa se a adição da largura da vez passa do limite, e ajusta apropriadamente a última largura.

size(400, 400)
background(0)
color_mode(HSB)
total = 0
while total < 400:
    largura = int(random(1, 32))
    if total + largura > 400:
        largura = 400 - total
    fill(largura * 8, 200, 200)
    rect(total, 0, largura, height)
    total += largura
    print(total)

Acumulando itens em uma coleção

Adicionando tuplas sorteadas à uma lista, dependendo de uma regra, a restrição da distância.

Suponha que você quer uma coleção de 1000 pontos, descritos por tuplas (x, y), produzidos (pseudo)aleatoriamente, mas que cumpram um requisito, no caso estar a uma certa distância do centro do desenho. A cada ciclo do laço é “sorteada” uma posição, mas ela é adicionada à lista apenas se cumprir o requisito. Como garantir que são adicionadas exatamente 1000 posições, uma vez que podem ser sorteadas posições que não atendem o requisito em alguns ciclos? A resposta é este uso de while.

posicoes = []  # uma lista para guardar tuplas de posições

def setup():
    size(400, 400)
    background(0, 0, 100)
    while len(posicoes) < 1000:
        x = random(width)
        y = random(height)
        if dist(x, y, 200, 200) < 195:
             posicoes.append((x, y))
             circle(x, y, 5)
    print(len(posicoes))  # exibe: 1000

pontos limitados pela distância

Note que neste exemplo, muito simples, não garantimos que não teremos posições sobrepostas. Isto pode ser resolvido consultando se a posição “sorteada” já existir na estrutura de dados. Para listas isso é algo não eficiente. Veja o exemplo a seguir, que resolve este problema, mas se trata de um desenho diferente, e a não-sobreposição é a única restrição.

Sorteando elementos que não se sobrepõe, as posições não repetem, usando um conjunto/set

Imagine uma grade com 6400 posições, vocẽ quer sortear exatamente 3200 quadrados, mas não quer sobreposições.

quadrados = set()  # conjunto, coleção que não preserva a ordem

def setup():
    size(400, 400)
    background(0, 0, 100)
    while len(quadrados) < 3200:
        x = int(random(width) / 5)
        y = int(random(height) / 5)
        if (x, y) not in squares:   # esta operação é rápida em conjuntos
             quadrados.add((x, y))    # note .add() e não .append()
             rect(x * 10, y * 10, 10, 10)
    print(len(quadrados))  # exibe: 3200

quadrados não sobretostos while

Passos não inteiros

No exemplo abaixo usaremos o laço while testando se o valor de um ângulo ang é menor que 360 graus (em radianos, a constante Pi vezes dois, ou, no Processing TWO_PI). O corpo do laço produz os vértices, que são pontos de um polígono em forma de estrela, a cada ciclo, e ang vai tendo o seu valor aumentado (ang += passo), até deixar de ser menor que TWO_PI, dessa forma encerrando o laço.

def setup():
    size(400, 400)
    background(0)
    estrela(width / 2, height / 2, 190, 100, 10)

def estrela(x, y, raio_a, raio_b, num_pontas):
    passo = TWO_PI / num_pontas
    begin_shape()
    ang=0
    while ang < TWO_PI:  # enquanto o ângulo for menor que 2 * PI:
        sx = cos(ang) * raio_a
        sy = sin(ang) * raio_a
        vertex(x + sx, y + sy)
        sx = cos(ang + passo / 2.) * raio_b
        sy = sin(ang + passo / 2.) * raio_b
        vertex(x + sx, y + sy)
        ang += passo  # aumente o ângulo um passo
    end_shape(CLOSE)

estrela

Uma variante com círculos

def setup():
    size(600, 600)
    background(0)
    mandala(width / 2, height / 2, 100, 10)

def mandala(x, y, raio, num_petalas):
    passo = TWO_PI / num_petalas
    angulo=0
    while angulo < TWO_PI:
        sx = x + cos(angulo) * raio
        sy = y + sin(angulo) * raio
        fill(255, 255, 255, 50)
        circle(sx, sy, raio * 2)
        angulo += passo

mandala

While e else

Uma sintaxe que pode ser usada também nos laços for, é uma cláusula else após o laço, o código neste bloco só executa se o laço transcorreu até o fim sem interrupção (sem um break).

while True: # um laço inicialmente infinito
    «corpo do laço, deve incluir
    efeito que afeta a condição de saída»
    if «condição de saída»:
        break # saída do laço    
else:
     «código que  executa se o while não foi interrompido»

Dois exemplos, em abstrato, que demonstram este comportamento.

x = 0
while x < 10:
    print(x)  # enquando a condição for verdadeira
    x += 1
else:  # quando a condição for falsa e o loop terminou
    print("False: o x não é mais menor que 10")

x = 0
while x < 10:
    print(x)  # enquando a condição for verdadeira
    x += 1
    if x > 8:
        break  # interrompe o laço antes!
else:  # quando a condição for falsa
    print("False: o x não é mais menor que 10")  # nunca será executado!

No segundo exemplo, break interrompe a execução do laço, e nesse caso, o bloco do else, que só executa quando a condição do while não é mais verdadeira, não será executado.

Assuntos relacionados