Looping

Em certo momento do jogo ‘A lenda de Zelda’ (1986), o herói Link se encontra perdido numa floresta (criativamente chama de Lost Woods).

zelda-lost-woods.png

Ao sair por qualquer um dos lados, Link parece voltar para o mesmo lugar, como se estivesse andando em círculos, ou preso em um loop. A única forma de sair da floresta é pegar as saídas em certa ordem: ao sair ao norte (e aparentemente voltar ao mesmo lugar), depois oeste (e aparentemente voltar ao mesmo lugar), depois sul (e aparentemente voltar ao mesmo lugar), e então novamente oeste, Link finalmente consegue sair da floresta.

Como poderíamos programar algo parecido? Para simplificar, vamos fingir que só há duas saídas (à esquerda e à direita), e se tentarmos sair pela esquerda conseguimos sair da floresta, mas se sairmos pela direita aparecemos novamente na floresta (mas do lado esquerdo).

Antes de tentarmos programar algo parecido com esta parte do jogo, vamos planejar o que vamos escrever. Ao desenhar programas ou algoritmos, é útil tentar esquematizar o que queremos antes de começarmos a programar de fato. Assim podemos ignorar os detalhes e focar no quadro geral. Quando fazemos esse planejamento na forma de código, chamamos ele de pseudocódigo. Abaixo encontra-se um possível pseudocódigo para o que queremos programar:

# Link está na floresta
if <sai pela direita>:
    <Link continua na floresta>
elif <sai pela esquerda>:
    <Link sai da floresta>

Qual o problema com esse (pseudo)código? A ideia está ali, mas não há loop; Link só faz uma ação e o programa termina. Podemos tentar consertar isso:

# Link está na floresta
if <sai pela direita>:
    <Link continua na floresta>
    if <sai pela direita>:
        <Link continua na floresta>
    elif <sai pela esquerda>:
        <Link sai da floresta>
elif <sai pela esquerda>:
    <Link sai da floresta>

Resolvemos o problema? Agora Link pode andar em círculos uma vez, mas não mais que isso, e o programa termina.

O que queremos escrever na verdade é uma forma de repetir indefinidamente o bloco if principal:

# Link está na floresta
if <sai pela direita>:
    <Link continua na floresta>
    if <sai pela direita>:
        <Link continua na floresta>
        …
    elif <sai pela esquerda>:
        <Link sai da floresta>
elif <sai pela esquerda>:
    <Link sai da floresta>

Uma forma de fazer isso é usando um while loop:

while <Link sai pela direita>:
    <Link continua na floresta>
<Link sai da floresta>

1. while loops

A sintaxe de while loops é muito simples: temos a keyword while seguida de uma condição, dois pontos, e então um novo bloco de código (lembre-se: um bloco é sempre marcado pela sua indentação).

A execução de um bloco while ocorre da seguinte forma: a condição é avaliada e o resultado deve ser do tipo booleano; se a condição for falsa, o bloco while é ignorado; se a condição for verdadeira (True), o bloco de código abaixo do while é executado, um passo de cada vez, e a condição do while é reavaliada, e repete-se a execução acima enquanto a avaliação da condição continuar sendo verdadeira.

Teste você mesmo o exemplo abaixo:

n = 0
user_says_go = True
while user_says_go:
    user_says = input("Digite 'continue' se quiser que o programa continue: ")
    if user_says == "continue":
      n = n + 1
      print("OK, vou rodar pela " + str(n) + "ª vez")
    else:
        user_says_go = False
print("Ok, parando")

1.1. Exercício: break

Python dispõe de uma keyword chamada break que pode ser usada para parar um loop. Se uma execução de um bloco while chega em uma keyword break, a execução para de avaliar o bloco ali mesmo, ‘pulando’ para o seu fim. Modifique o exemplo anterior, removendo o uso da variável user_says_go, e usando um break para obter o mesmo comportamento.

1.2. Exercício: calculando várias taxas de performance interativamente

Modifique o código que calcula a taxa de performance de um fundo para que ele ofereça para calcular uma nova taxa (para um outro fundo) enquanto o usuário não pedir para sair do programa. Ou seja, o programa pede as informações referentes a um fundo & benchmark, mostra os resultados de suas contas, e então volta a pedir informações referentes a um novo fundo & benchmark, e assim por diante, até o usuário pedir para sair.

Para que o usuário tenha uma forma de pedir para sair, você pode usar um prompt como este:

investimento_inicial = input("Valor inicial do investimento (ou digite SAIR para sair): ")

Assim, se o usuário digitar SAIR e apertar a tecla Enter, a variável investimento_inicial conterá uma string igual a 'SAIR' (e você pode usar um if para continuar o cálculo ou parar).

pede-pra-sair.jpg
Figura 1: Usuário e programa (imagem meramente ilustrativa)

2. for loops

Se o jogo ‘A lenda de Zelda’ fosse mais realista, Link não poderia ficar preso indefinidamente na floresta. Em algum momento ele desmaiaria de fome e/ou sede. Assim, os desenvolvedores do jogo poderiam ter colocado um contador para o número limite de vezes que Link poderia tomar o caminho errado. Podemos fazer isso com um while:

lost_limit = 5
while <Link sai pela direita>:
    lost_limit = lost_limit - 1
    if lost_limit > 0:
        <Link continua na floresta>
    else:
        <link faints> # game over
<Link sai da floresta>

Não há nada de errado em usar um while em um caso como esse, mas note que é preciso inicializar a variável contadora (lost_limit = 5) antes de começar o loop. Além disso, precisamos tomar cuidado com o momento em que decrementamos o contador. Para perceber isto, note: quantas vezes no máximo este loop pode rodar? E se decrementássemos o contador dentro da primeira cláusula do bloco if?

Quando queremos repetir um bloco de código um número conhecido de vezes, a melhor opção é usar um for loop, como no pseucódigo abaixo:

for _n in range(5):
    if <Link sai pela direita>:
        <Link continua na floresta>
    else:
        <Link sai da floresta>
if <Link ainda na floresta>:
    <link faints> # game over

A sintaxe de for loops é a seguinte: temos a keyword for seguida de um nome de variável, a keyword in, e a chamada range(<número>) seguida de dois pontos, e então um bloco de código (lembre-se: um bloco é sempre marcado pela sua indentação). <número> pode ser qualquer inteiro.

A execução de um bloco for ocorre exatamente <número> vezes, a menos que a keyword break seja usada em algum momento (ou um erro aconteça). O nome de váriavel escolhido recebe o valor do ‘número’ da execução. Teste você mesmo:

for i in range(7):
    print(i)

Como você pode ver rodando o código, a variável i toma o valor de 0, e então 1, …, até 6, quando o bloco do loop executa pela sétima e última vez.

Uma outra forma de entender a execução de um bloco for é perceber que ele é apenas uma forma mais simples de executar alguns tipos de blocos while.

for i in range(n):
  print(i)

faz a mesma coisa que

i = 0
while i < n:
    print(i)
    i = i + 1

E como podemos ver, usar o for nesse caso é mais curto, e por isso também é mais difícil de errar. Apesar de ser sempre possível usar um while no lugar de um for, o contrário não é verdade (vide o exemplo do cálculo repetido da taxa de performance).

2.1. range

range na verdade é mais versátil do que mostrei: sua forma completa é range(start, stop, step):

# mostra os números pares de 0 à 10 (exclusive)
for i in range(0, 10, 2):
    print(i)

Quando escrevemos range(n), o Python assume que estamos escrevendo range(0, n, 1), ou seja, que queremos que i varie de 0 até n (exclusive), assumindo valores intermediários sempre 1 (o step) maiores do que os anteriores.

Podemos também usar range da forma range(start, stop), ‘forçando’ o loop a contar a partir do número 1:

for i in range(1, 7):
    print(i)

Mas cuidado: range só conta até o número exclusive, então o for loop acima executa 6 vezes ao invés de 7. Se quisermos mostrar os números de 1 a 7, podemos fazer

for i in range(1, 8):
  print(i)

ou

for i in range(0, 7):
  print(i + 1)

2.2. Exercício: sequência de Fibonacci

A famosa sequência de Fibonacci é uma favorita dos programadores. Ela é definida por uma relação de recorrência: \[F_1 = 0\] \[F_2 = 1\] \[F_n = F_{n-1} + F_{n-2} \qquad (n > 2)\]

Ou seja, \(F_1\) (o primeiro número de Fibonacci) é o zero, \(F_2\) é 1, \(F_3\) também é 1, \(F_4\) é 2, \(F_5\) é 3, e assim por diante.

Como exercício, calcule o n-ésimo número da sequência de Fibonacci. Use input para obter o valor de uma variável n cujo valor é o número do índice da sequência desejado; o código deve calcular o número de Fibonacci cujo índice na sequência é n. Por exemplo, se n == 5, o código deve printar 3. O código deve terminar de rodar e printar o valor correto para qualquer número inteiro maior que zero.

3. Comparando for e while

for while
número de iterações conhecido número de iterações indefinido
pode-se usar break para terminar cedo pode-se usar break para terminar cedo
usa um contador usa uma condição, que pode envolver um contador (precisa ser declarado antes do loop)
sempre pode ser reescrito como um loop while nem sempre pode ser transformado em um loop for

3.1. Exercício: transformando for em while (e vice-versa)

Escreva uma outra versão do seu código que calcula o n-ésimo número da sequência de Fibonacci:

  • se você tiver usado for, desta vez use um while;
  • se você tiver usado while, desta vez use um for.
Bruno Cuconato / 2024-08-07
TOP | HOME
Computação FGV/EPGE