Controle do Fluxo de Execução


Introdução

Até agora, nossos programas consistiram de uma série de comandos executados sequencialmente. Em outras palavras, a execução dos nossos programas até aqui seguiu um fluxo sequencial.

Nesta seção aprenderemos a alterar o fluxo de execução dos nossos programas. Em outras palavras, veremos como fazer nossos programas tomarem decisões dependendo de certas condições, bem como executar certos comandos repetidas vezes. Os recursos da linguagem que nos permitem fazer isso são o que chamamos de estruturas condicionais e estruturas de repetição.

“Fluxo de controle”

O primeiro recurso de controle de fluxo que aprenderemos são os condicionais. Em seguida, aprenderemos a usar estruturas de repetição.

Estruturas Condicionais

Uma capacidade importantíssima de uma linguagem de programação, necessária em praticamente todo programa de computador, é a capacidade de tomar decisões baseadas em certas condições.

Condicionais com if e else

A linguagem Python usa algumas palavras-chave para indicar execução condicional de código: if, elif (else if), else. Basicamente, os condicionais nos permitem selecionar certos blocos de código que serão ou não executados dependendo de certas condições. Em alto nível, um condicional é como a sequência de passos abaixo:

  1. A condição é verdadeira?
  2. Se sim, execute um trecho de código.
  3. Caso contrário, execute outro trecho de código.

Vejamos alguns exemplos.

def imprime_condicao(condicao):
  if condicao == True:
      print("A condicao é verdadeira.")
  else:
      print("A condicao é falsa.")

imprime_condicao(False)
imprime_condicao(True)
A condicao é falsa.
A condicao é verdadeira.

Em Python, fazer if condicao == True é equivalente a fazer if condicao, mas a segunda forma é mais usada. Talvez por ser mais sucinta, algumas pessoas costumam dizer que é um jeito mais “pythonico” de se escrever o código. Acostume-se com isso, pois você verá muito código escrito desse jeito. Vejamos um exemplo de como seria o código acima escrito dessa forma mais “pythonica”:

def imprime_condicao(condicao):
  if condicao:
      print("A condicao é verdadeira.")
  else:
      print("A condicao é falsa.")

imprime_condicao(False)
    A condicao é falsa.

Condicionais com if elif e else

Condicionais também podem ser usados em cadeia (um após o outro), como mostrado abaixo:

def imprime_multiplas_condicoes(condicao_1, condicao_2):
  if condicao_1:
    print("A condicao_1 é verdadeira.")
  elif condicao_2:
    print("A condicao_2 é verdadeira.")
  else:
    print("As condições condicao_1 e condicao_2 são falsas.")


condicao_1 = False
condicao_2 = False
imprime_multiplas_condicoes(condicao_1, condicao_2)
As condições condicao_1 e condicao_2 são falsas.

Pelos exemplos acima, vimos que blocos de código em Python são delimitados pelo nível de indentação deles. Os comandos print estão alinhados mais à direita no exemplo acima, e isso não é por acaso. Isso é chamado de indentação (o correto é indentação mesmo, e não “identação”) e ela serve para delimitar blocos de código.

Em linguagens como C e Java, blocos de código são delimitados por {}, e a indentação do código é simplesmente uma boa prática de programação, mas não é necessária para o correto funcionamento dos programas. Até mesmo linguagens de marcação usam certos delimitadores para blocos de código. Em HTML, por exemplo, blocos de código são delimitados por <…​>.

É importante enfatizar isso: em Python, blocos de código são delimitados por sua indentação. E a indentação é parte integrante do programa, sendo essencial para seu correto funcionamento.

def indentacao_incorreta():
  # Má indentação!
  if sentenca_1:
      if sentenca_2:
      # A linha abaixo não está indentada corretamente.
      print("Ambas sentenças são verdadeiras.")

Ao tentarmos executar a função acima receberemos o seguinte erro:

    File "<ipython-input-227-30e2d674efad>", line 4
    # A linha abaixo não está indentada corretamente.
    print("Ambas sentenças são verdadeira.")
        ^
IndentationError: expected an indented block

Vejamos agora um exemplo em que a indentação incorreta nos leva a obter o resultado errado.

def foo(s):
  if s:
      print("Impresso se s for verdadeiro.")

      print("Ainda dentro do if. Note a indentação.")

foo(True)
Impresso se s for verdadeiro.
Ainda dentro do if. Note a indentação.

Agora definindo a função de forma correta, obtemos o resultado desejado.

def foo(s):
  if s:
      print("Impresso se s for verdadeiro.")

  print("Agora fora do if. Note a indentação.")

foo(True)
Impresso se s for verdadeiro.
Agora fora do if. Note a indentação.
📌 NOTA

No exemplo acima, demos o nome foo para a nossa função. Apesar de estranho, isso é uma prática comum em exemplos de programação. Sempre que quisermos definir uma função qualquer para ilustrar, sem nos preocuparmos com o que ela efetivamente faz, damos o nome de foo à função. Outros nomes de função usados em situações parecidas são bar e baz. Quando vir funções com esses nomes, lembre-se de que quem as escreveu quer ilustrar um ponto específico que não depende do nome da função.

Condicionais com match

A declaração match em Python é uma adição recente, introduzida no PEP 634 e disponível a partir da versão 3.10. Ela fornece uma maneira mais expressiva e concisa de realizar correspondência de padrões em comparação com o tradicional if-elif-else visto anteriormente.

A estrutura da declaração match é semelhante à de um switch em outras linguagens de programação, permitindo a correspondência de um valor contra vários padrões e a execução do bloco de código associado ao primeiro padrão correspondente.

Vamos explorar um exemplo simples de uso da declaração match para determinar o tipo de uma variável:

def verifica_tipo(valor):
    match valor:
        case int:
            print("É um número inteiro.")
        case float:
            print("É um número de ponto flutuante.")
        case str:
            print("É uma string.")
        case _:
            print("Tipo não reconhecido.")

# Exemplos de uso
verifica_tipo(42)
verifica_tipo(3.14)
verifica_tipo("Python")
verifica_tipo(True)
É um número inteiro.
É um número de ponto flutuante.
É uma string.
Tipo não reconhecido.

Neste exemplo, a declaração match é usada para verificar o tipo da variável valor e executar o bloco de código correspondente ao tipo encontrado. O padrão _ atua como um coringa, capturando qualquer valor que não corresponda aos padrões anteriores.

A declaração match é útil para simplificar certos trechos de código, tornando-os mais simples e evitando a repetição de estruturas if-elif-else. Essa adição é particularmente útil em situações onde há muitos casos para verificar, tornando o código mais legível e fácil de manter.

Estruturas de Repetição

Outra capacidade importantíssima de linguagens de programação é a repetição de comandos até que uma dada condição seja satisfeita. As linguagens de programação implementam isso por meio dos chamados laços de repetição.

Laços for

Existem vários tipos de laços (loops) em Python. O mais comum deles é o for, que é usado com objetos iteráveis tais como listas e intervalos. Os exemplos abaixo mostram o funcionamento básico do for.

Vejamos agora como iterar em intervalos em Python.

def imprime_intervalo():
  # Percorrendo um intervalo. Por padrão, intervalos começam em zero.
  for i in range(3):
      print(i)

imprime_intervalo()
0
1
2

Observação: range(3) não inclui o 3, como mencionamos anteriormente.

Nos exemplos acima, ao percorrer os elementos de uma sequência, não usamos o índice do elemento (sua posição na sequência). Entretanto, há situações em que esta informação é útil. Python nos permite acessar tanto os índices dos elementos quanto os elementos propriamente ditos se usarmos a função enumerate.

def imprime_indice_e_elemento():
  for indice, elemento in enumerate(range(-3,3)):
      print(indice, elemento)

imprime_indice_e_elemento()
0 -3
1 -2
2 -1
3 0
4 1
5 2

Laços while

Além dos laços for, Python conta também com os laços while, que executam enquanto uma dada condição for verdadeira:

def imprime_intervalo_while():
  i = 1
  while i < 5:
      print(i)
      i = i + 1

def imprime_intervalo_while()
1
2
3
4

Um exemplo útil de uso de laços while é o cálculo do fatorial de um número:

def fatorial(n):
  fatorial = 1
  while n > 1:
    fatorial *= n
    n -= 1
  return fatorial

fat_6 = fatorial(6)
print(fat_6)
720

Note que:

  • fatorial *= n é equivalente a fatorial = fatorial * n
  • n -= 1 é equivalente a n = n - 1

Exercícios

  1. Escreva uma função que recebe dois parâmetros e imprime o menor dos dois. Se eles forem iguais, imprima que eles são iguais.
Clique para ver a solução
def imprime_menor(a, b):
    if a < b:
      print(a)
    elif a > b:
      print(b)
    else:
      print("Os números são iguais.")

imprime_menor(0, 5)
imprime_menor(10, 3)
imprime_menor(42, 42)
0
3
Os números são iguais.
  1. Escreva uma função que recebe um número n como parâmetro e imprime se n é positivo ou negativo.
Clique para ver a solução
def imprime_sinal(n):
    if n < 0:
      print("Negativo")
    else:
      print("Positivo")

imprime_sinal(-1)
imprime_sinal(5)
Negativo
Positivo
  1. Escreva uma função para imprimir o valor absoluto de um número.
Clique para ver a solução
def imprime_valor_absoluto(n):
    if n < 0:
      n = -n
    print(n)

imprime_valor_absoluto(-5)
imprime_valor_absoluto(10)
imprime_valor_absoluto(-1)
5
10
1
  1. Escreva uma função que recebe dois números a e b como parâmetro e retorna True caso a soma dos dois seja maior que um terceiro parâmetro, chamado limite.
Clique para ver a solução
def soma_maior_que_limite(a, b, limite):
    if a + b > limite:
      return True
    else:
      return False

print(soma_maior_que_limite(10, 5, 15))
True

Perceba que a função acima retorna exatamente o resultado da comparação a + b > limite, isto é, se a + b for maior que limite, a função retorna True e caso contrário ela retorna False. Portanto, nossa função poderia ser escrita de forma mais sucinta assim:

def soma_maior_que_limite_sucinta(a, b, limite):
    return (a + b) > limite

print(soma_maior_que_limite_sucinta(10, 5, 15))
True

Há quem diga que a primeira versão da função é mais legível, mas há também quem prefira a segunda versão. Independentemente da sua preferência, você encontrará códigos parecidos com as duas versões ao longo do seu aprendizado, então é importante saber entender esse tipo de código mais conciso.

Note que usamos parênteses para delimitar a soma de a e b, mas se você se recordar da seção sobre precedência de operadores, você verá que os parênteses são desnecessários, pois o operador + tem uma precedência maior que o operador >, portanto a soma será executada antes da comparação. Assim, a função acima pode ser definida da seguinte forma, sem que seu comportamento seja alterado:

def soma_maior_que_limite_sucinta(a, b, limite):
    return a + b > limite
  1. Escreva uma função que recebe dois números (a e b) como parâmetro e retorna a quantidade (0, 1 ou 2) deles que é maior que um terceiro parâmetro, chamado limite.
Clique para ver a solução
def num_parametros_maior_que_limite(a, b, limite):
    if a > limite and b > limite:
      return 2
    elif a > limite or b > limite:
      return 1
    else:
      return 0

print(num_parametros_maior_que_limite(50, 7, 30))
1
  1. Escreva uma função que recebe um número como parâmetro e para cada número menor que o parâmetro, a função imprime “Fizz” se o número for múltiplo de três, imprime “Buzz” se o número for múltiplo de cinco, e imprime “FizzBuzz” se o número for múltiplo de três e cinco. Caso o número não seja múltiplo nem de três nem de cinco, ele deve ser impresso. Note que, ao contrário das funções anteriores, sua função não deve retornar nada. Ela precisa simplesmente imprimir o que foi pedido.
Clique para ver a solução
def fizz_buzz(n):
    for num in range(n):
        if num % 3 == 0 and num % 5 == 0:
            print('FizzBuzz')
        elif num % 3 == 0:
            print('Fizz')
        elif num % 5 == 0:
            print('Buzz')
        else:
            print(num)

print(fizz_buzz(16))
FizzBuzz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

Perceba que testamos primeiro se o número é divisível por 3 e 5 ao mesmo tempo antes de testarmos se ele é divisível por cada um desses números individualmente. Tente relembrar das regras da Matemática e pense por que fizemos isso. Essa é uma pergunta simples, mas que aparece em entrevistas de programação, então separe um tempo e tente entender bem esse exercício.

  1. Escreva uma função que, dado um número nota representando a nota de um estudante, converte o valor de nota para um conceito (A, B, C, D, E e F).
Clique para ver a solução
def converte_nota_em_conceito(nota):
    if nota >= 90:
      return "A"
    elif nota >= 80:
      return "B"
    elif nota >= 70:
      return "C"
    elif nota >= 60:
      return "D"
    elif nota >= 40:
      return "E"
    else:
      return "F"

print(converte_nota_em_conceito(65))
D

Note que no exemplo acima, como sempre retornamos um valor dentro de cada teste, não precisamos fazer testes como if nota >= 60 and nota <= 69. Se a nota for maior que 90, ela será retornada no primeiro teste. Caso não seja, mas seja maior que 80 (nesse caso, implicitamente ela está entre 80 e 90) ela é retornada no segundo teste, e assim por diante. Se estivéssemos imprimindo a nota dentro da função (ao invés de retorná-la) não poderíamos ter escrito o código dessa forma. Talvez essa não seria a forma que você escreveria essa função (e há meritos na outra forma, que é mais explicita), mas é importante entender esse tipo de código. Programação não é só escrever código, entender código dos outros é extremamente importante também.

Desafios

  1. Escreva uma função que recebe como entrada um número inteiro positivo n e retorne a soma de todos os inteiros positivos menores ou iguais a n.

  2. Escreva uma função que recebe como entrada um número ano e retorna True caso ano seja bissexto. Caso contrário, retorne False.

  3. Escreva uma função que recebe como entrada um número n e imprime todas as potências de 2 menores ou iguais a n.

  4. Escreva uma função que recebe como entrada um número inteiro positivo n e imprime a representação binária desse número.