Laços de Repetição - #14
Tags: beginners, programming, python, tutorial
Published: 2022-07-12 23:04:01

{% youtube https://youtu.be/lHlznUnmCTs %}

## Estruturas de repetição

### Sintaxe _while_

A cláusula de repetição **_while_** permite que um bloco de código seja executado, repetidamente, enquanto a condição for verdadeira (**_True_**). Sua sintaxe contem:
* palavra reservada **_while_**
* uma _condição_ ou _expressão booleana_
* dois pontos (_:_)
* bloco de código indentado (4 espaços), a ser executado se a condição **_while_** é **_True_**.

```python
#Sintaxe while
while condição :
    instruções
```

Algoritmo para imprimir os números de 1 a 10:

```python
#Algoritmo - imprimir de 1 a 10
num = 1
while num <= 10:
    print(num)
    num = num+1
```

Na linha 02 temos a instrução **_while_** com a condição de que enquanto o valor da variável _num_ for menor ou igual a 10, as instruções das linhas 03 e 04 serão executadas. Note a indentação ou os 4 espaços necessários para identificar as instruções que serão executadas repetidamente.

Quando trabalhamos com o bloco **_while_** é importante controlamos a cláusula condicional, para que ela se torne falsa (**_False_**) e finalize a execução do seu laço. Caso contrário teremos um **_loop_** infinito, seu código nunca terminará de executar.

Chamamos de **_iteração_** cada vez que o laço ou o bloco de código é executado. Do exemplo anterior, o laço possui 10 iterações, e antes que cada iteração seja executada, a cláusula condicional é avaliada.

Veja o exemplo de um **_loop_** que não se encerra enquanto o usuário não digita a palavra **_Python_**:

```python
#Algoritmo - qual linguagem? (qualLinguagem.py)
linguagem = ""
while linguagem != "Python":
    print("Qual linguagem estamos estudando? ")
    linguagem = input()
print("Parabens! Voce acertou!")
```

Na linha 01 iniciamos uma variável do tipo **_string_** com valor em branco. Na linha 02, iniciamos o nosso bloco **_while_**, que executará o bloco de código das linhas 03 e 04, enquanto o valor da variável _linguagem_ não é _Python_.

Somente quando o bloco de repetição for finalizado (quando o usuário digitar _Python_), a linha 05 será executada.

```
#Exemplo - execução de qualLinguagem.py 
Qual linguagem estamos estudando?
c
Qual linguagem estamos estudando?
C++
Qual linguagem estamos estudando?
Java
Qual linguagem estamos estudando?
python
Qual linguagem estamos estudando?
Python
Parabens! Voce acertou!
```

## Atribuição composta

A atribuição composta é uma forma simplificada de reutilizar a mesma variável nas operações aritméticas, como por exemplo +=, -=, *=, /=. 

A expressão 2, abaixo, é a simplificação da expressão 1.

```
#Exemplo - sintaxe atribuição composta
#expressão 1
num = num + 5;

#expressão 2
num += 5;
```

Experimente algumas atribuições compostas no **_shell_**.

```python
#Atribuição composta no shell
>>> num = 10
>>> print(num)
10

>>> num += 5
>>> print(num)
15

>>> num *= 2
>>> print(num)
30

>>> num %= 2
>>> print (num)
0
```

Vamos a outro exemplo, é comum o uso do laço **_while_** para deixar um programa executando até que o usuário digite algum valor para sair e finalizar a execução.

No exemplo abaixo, enquanto o usuário não digitar **_sair_**, o programa continuará executando, irá imprimir na tela o que o usuário digitou.

```python
#Algoritmo - executeDigiteSair.py
texto = "\n Digite 'sair' para encerrar o programa"
texto += "\n Digite alguma mensagem: "

mensagem = ""

while mensagem != "sair":
    mensagem = input(texto)
    print(mensagem)
```

Criamos 2 variáveis, a variável _texto_ para imprimir uma mensagem padrão ao usuário e a variável _mensagem_ que receberá o valor digitado pelo usuário.

Observe o uso do “\n” para escrever em uma nova linha, e o uso do “+=” nas linhas 01 e 02.

Na linha 06 temos nosso laço principal, enquanto o valor de _mensagem_ não for _“sair”_, o **_texto_** será mostrado e o usuário deverá digitar algo que será impresso na tela.

```
#Execução - executeDigiteSair.py 
 Digite 'sair' para encerrar o programa
 Digite alguma mensagem: Alguma coisa
Alguma coisa

 Digite 'sair' para encerrar o programa
 Digite alguma mensagem: teste
teste

 Digite 'sair' para encerrar o programa
 Digite alguma mensagem: sair
sair
```

### Sintaxe _for_

O laço **_for_** é uma estrutura de repetição alternativa ao laço **_while_**. Ele geralmente é aplicado quando já sabemos a quantidade de interação que vamos executar.

Sua sintaxe contem:
* palavra reservada **_for_**
* um nome de variável
* a palavra reservada **_in_**
* a chamada da função **_range_** que pode receber até três argumentos ou valores
* dois pontos (_:_)
* bloco de código indentado (4 espaços),  a ser executado enquanto  a _condição_ é **_True_**.

```
#Sintaxe for
for <variavel> in range(<valor 1>, <valor 2>, <valor 3>):
    instruções
```

Algoritmo para imprimir os números de 1 a 10:

```python
#Algoritmo - imprimir de 1 a 10 usando for
for num in range(10):
    print(num+1)
```

Para imprimirmos de 1 a 10 usando o **_for_** (linha 01), basta definirmos uma variável **_num_** que irá variar no intervalo de 10 (**_range(10)_**). A função **_range_** gerará números de ZERO a 9, por isso foi necessário na linha 02 somar o valor de **_num_** com 1.

## Instruções **_break_** e **_continue_**

As instruções **_break_** e **_continue_** são palavras reservadas que só podem ser utilizadas dentro de laços de repetição. 

### Instrução **_break_**

A instrução **_break_** quando é executada, encerra a execução de um laço de repetição. Quando o intepretador _Python_ encontra um comando **_break_**, automaticamente ele sairá de dentro do **_loop_** e continuará a execução normal do programa.

O exemplo abaixo irá imprimir somente até o número 7:

```python
#Algoritmo - imprimir de 1 a 10 modificado com break
num = 1
while num <= 10:
    print(num)
    if num == 7:
        break
    num = num+1
```

Veja que o condicional do **_while_** é verdadeiro enquanto o _num_ é menor ou igual a 10 (linha 02). Na linha 04 se o _num_ é igual a 7, o comando **_break_** será executado e encerrará a execução do **_loop_**.

Também usando o **_break_**, podemos implementar outra solução para o problema **_qual linguagem_**:

```python
#Algoritmo - qual linguagem com break
linguagem = ""
while True:
    print("Qual linguagem estamos estudando? ")
    linguagem = input()
    if linguagem == "Python":
        break
```

É muito comum encontrarmos algoritmos com uma condição _infinita (while True)_, como do exemplo acima, mas no bloco de instruções temos o **_break_** para encerrar a execução do laço.

### Instrução **_continue_**

Da mesma forma que a instrução **_break_**, a instrução **_continue_** deve estar dentro de um laço de repetição.

Quando uma instrução **_continue_** é encontrada, a execução da interação corrente é interrompida e a próxima interação executada.

Podemos imprimir os números múltiplos de 5 entre 1 e 30, usando o **_continue_**:

```python
#Algoritmo - múltiplos de 5 entre 1 e 30
num = 0
while num <= 30:
    num = num + 1
    if (num % 5 != 0):
        continue
    print(num)

```

O algoritmo acima não é a única e nem a melhor forma de se imprimir os múltiplos de 5, mas foi escrito para explorarmos a sintaxe do **_continue_** da linha 05.

Na linha 01 inicializamos uma variável numérica **_num_** com valor ZERO e enquanto esta variável possuir o valor menor ou igual a 30 (bloco **_while_** - linhas 02 a 06), ela será incrementada de 1 unidade e na linha 04 se o resto da divisão for diferente de ZERO, a instrução **_continue_** será executada. A instrução **_continue_** sendo executada, automaticamente a linha 06 não será chamada (número não será impresso) e o laço executará a próxima iteração.

```
#Execução - múltiplos de 5 entre 1 e 30 
5
10
15
20
25
30
```

## Função **_range( )_**

Chamamos de **_argumento_** os valores que passamos para uma função. 

Vimos que algumas funções não possuem argumentos, como o caso da função **_input( )_** que espera algo ser digitado no teclado do computador, e outras que recebem um parâmetro como as funções **_print_**(‘Texto’) ou as funções **_str(valor), int(texto)_**.

A função **_range_** utilizado nos laços de repetição **_for_**, pode receber **até 3 argumentos**, representando:
* ponto de início do laço (inclusivo)
* ponto final do laço (exclusivo)
* incremento da iteração

```
#Sintaxe range
range(argumento1, argumento2, argumento3)
```

### Sintaxe _range(argumento 1)_

A função **_range( )_** quando possui somente 1 argumento, o utilizará como ponto final do laço, não incluindo o valor.

```python
#Algoritmo - usando range com um argumento
for i in range(5):
    print(i)
```

O laço iniciará do valor ZERO e o incremento será de 1 unidade.

```
#Execução - usando range com um argumento 
0
1
2
3
4
```

Note que o argumento é o valor 5 (linha 01) mas ele não será utilizado, o **_range_** será de ZERO a 4.

### Sintaxe _range(argumento 1, argumento 2)_

Quando usamos 2 argumentos na função **_range( )_**, o primeiro argumento será o valor inicial e o segundo argumento o valor final não incluso da interação.

```python
#Algoritmo - usando range com dois argumentos
for i in range(20, 30):
    print(i)
```

Novamente o incremento será de 1 unidade.

```
#Execução - usando range com dois argumentos 
20
21
22
23
24
25
26
27
28
29
```

Com o **_range(20, 30)_** serão impressos os valores de 20 a 29. O número 30 não é incluído.

Se você possuir um **_range_** decrescente, com 2 argumentos, não haverá execução do laço.

<table>
  <tr>
   <td>Agora é a sua vez!
   </td>
  </tr>
  <tr>
   <td>Reescreva o algoritmo “<strong><em>usando range com dois argumentos”</em> </strong>utilizando um <strong><em>range(30, 20)</em></strong>.
<p>
O que acontece na execução? Você receberá algum erro?
<p>
Experimente!
   </td>
  </tr>
  <tr>
   <td>
   </td>
  </tr>
</table>

### Sintaxe _range(argumento 1, argumento 2, argumento 3)_

Na sintaxe completa da função **_range( )_** recebendo 3 argumentos, teremos o argumento 1 como ponto inicial, o argumento 2 como valor final exclusivo e o argumento 3 será o incremento em cada iteração.

Para entendermos, veja o exemplo abaixo imprimindo os valores de 0 a 30, de 5 em 5:

```python
#Algoritmo - usando range com trê argumentos
for i in range(0, 31, 5):
    print(i)
```

Com o terceiro argumento igual a 5, o incremento será de 5 unidades.

```
#Execução - usando range com três argumentos 
0
5
10
15
20
25
30
```

O argumento de valor de iteração é flexível, aceitando valores negativos:

```python
#Algoritmo - usando range com trê argumentos, negativo
for i in range(30, 9, -5):
    print(i)
```

Com o terceiro argumento igual a -5, o incremento será subtraindo 5 unidades. Ou seja, com valores negativos, teremos um decremento.

```
#Execução - usando range com três argumentos, negativo
30
25
20
15
10
```

## Gerando listas numéricas com **_range()_**

O método **_range()_** pode ser utilizado para gerar listas numéricas.

```python
#Sintaxe range para gerar listas
<lista> = list(range(<arg1>, <arg2>, <arg3>))
arg2 e arg3 são opcionais no range()
```

Veja alguns exemplos usando o **_range()_** na criação de listas, <span style="text-decoration:underline;">o código abaixo não consegue ser executado no **_shell_**</span>, você precisa criar o arquivo **_.py_**.

```python
#Algoritmo - gerarListas.py
print("Exemplo 01")
lista = list(range(5))
print(lista)

print("Exemplo 02")
lista = list(range(10, 15))
print(lista)

print("Exemplo 03")
lista = list(range(30, 41, 2))
print(lista)
```

```
#Execução - gerarListas.py
Exemplo 01
[0, 1, 2, 3, 4]
Exemplo 02
[10, 11, 12, 13, 14]
Exemplo 03
[30, 32, 34, 36, 38, 40]
```

### Funções **_max(), min(), sum()_**

Com listas numéricas, podemos aplicar algumas funções úteis da linguagem, como:
* **_max()_**, retorna o maior número da lista.
* **_min()_**, retorna o menor número da lista.
* **_sum()_**, retorna a soma dos números da lista.

```python
#Execução - max(), min(), sum() no shell
>>> numeros = [4, 2, 8, 1]

>>> max(numeros)
8

>>> min(numeros)
1

>>> sum(numeros)
15
```

## Percorrendo listas com **_for_**

Agora que sabemos o laço **_for_**, podemos utilizá-lo para acessar cada elemento de uma lista.

```python
#Sintaxe for para percorrer listas
for <variavel_item> in <lista>:
    instruções
```

```python
#Algoritmo - imprimindo elementos de uma lista
#Imprime cada elemento de uma lista e calcula seu dobro
lista_numeros = [6, -20, 12, 1]
for num in lista_numeros:
    print(f"Elemento: {num}")
    print("O dobro: " + str(2 * num))
```

Na linha 01 temos um comentário indicando a funcionalidade do algoritmo, que define uma lista de números (linha 02), e no laço **_for_**, percorre cada item da lista e usa a variável **_num_** para armazená-los.

As instruções das linhas 04 e 05 pertencem ao bloco **_for_**, identificados pela identação ou 4 espaços, e imprimem o valor de **_num_** (linha 04) e o seu dobro (linha 05).

```
#Execução - imprimindo elementos de uma lista 
Elemento: 6
O dobro: 12
Elemento: -20
O dobro: -40
Elemento: 12
O dobro: 24
Elemento: 1
O dobro: 2
```

Também é possível usar o laço **_for_** e percorrer **sublistas**. Por exemplo, dada uma lista de nomes, imprima os 3 primeiros e os 3 últimos elementos.

```python
#Algoritmo - top3.py
ranking = ["Joao", "Jose", "Maria", "Igor", "Paulo", "Laura", "Andre", "Alex"]

print("Top 3: ")
for nome in ranking[:3] :
    print("\t" + nome)

print("Last 3: ")
for nome in ranking[-3:] :
    print("\t" + nome)
```

Nas linhas 04 e 09 usamos a sintaxe de sublistas para ler os elementos, também observe o **_“\t”_** para inserir 4 espaços na impressão da tela.

```
#Execução - top3.py 
Top 3: 
	Joao
	Jose
	Maria
Last 3: 
	Laura
	Andre
	Alex
```

### Usando função **_enumerate()_**

A função **_enumerate()_** retorna o índice e o item da lista.

```python
#Algoritmo - exemploEnumerate.py
lista_cor = ["preto", "branco", "vermelho"]
for indice, cor in enumerate(lista_cor):
    print(indice, cor)
```

```
#Execução - exemploEnumerate.py 
0 preto
1 branco
2 vermelho
```

## Preenchendo listas com **_while_**

Podemos usar uma lista **_while_** para preencher uma lista com valores.

```python
#Algoritmo - listaAmigos.py
amigos = []
while True:
    print("Digite o nome do amigo " + str(len(amigos) + 1) +
            " ou enter para sair:")
    name = input()
    if name == "":
        break  
    amigos.append(name)
print("Lista de amigos:")
for amigo in amigos:
    print("\t" + amigo)
```

No algoritmo anterior, preenchemos a lista **_amigos_** enquanto o usuário fornece algum valor. Através do uso do laço **_while_**, pedimos ao usuário que forneça o nome de um amigo, e no laço condicional da linha 06, se o valor for vazio, executamos o **_break_** e saímos do laço **_while_**.

Finalmente na linha 09, fora do bloco de repetição, imprimimos na tela a lista de amigos.

```
#Execução - listaAmigos.py 
Digite o nome do amigo 1 ou enter para sair:
Amigo 1
Digite o nome do amigo 2 ou enter para sair:
Joao
Digite o nome do amigo 3 ou enter para sair:
Lara
Digite o nome do amigo 4 ou enter para sair:
Paula
Digite o nome do amigo 5 ou enter para sair:

Lista de amigos:
        Amigo 1
        Joao
        Lara
        Paula
```

## Erros comuns

Cuidado com **_loops_** infinitos, situação em que seu código nunca terminará de executar. 

Veja o exemplo abaixo, usando o laço **_while_**, no qual a variável **_num_** nunca é atualizado, e nunca será maior que 10:

```python
#Algoritmo - imprimir de 1 a 10 em loop infinito
01
02
03
num = 1
while num <= 10:
    print(num)
```

Se você executar o código acima, seu programa irá imprimir o número 1 infinitamente. Para forçar o encerramento do programa, você deve digitar _CTRL + C_ ou _CTRL + Z_.

A indentação continua importante, as instruções são identificadas através dos 4 espaços do bloco. O algoritmo abaixo é executado mas com o comportamento diferente do esperado, a linha 05 não pertence ao bloco **_for_**.

```python
#Algoritmo - indentação incorreta
#Imprime cada elemento de uma lista e calcula seu dobro
lista_numeros = [6, -20, 12, 1]
for num in lista_numeros:
    print(f"Elemento: {num}")
print("O dobro: " + str(2 * num))
```

```
#Execução - indentação incorreta
Elemento: 6
Elemento: -20
Elemento: 12
Elemento: 1
O dobro: 2
```

E não esqueça dos “:” da sintaxe.

```python
#Erro de sintaxe - faltando os “:”
>>> for x in lista_nomes
  File "<stdin>", line 1
    for x in lista_nomes
                        ^
SyntaxError: expected ':'
```