Revisão: Fundamentos
Objetivos de Aprendizagem
Ao final desta aula, você deve ser capaz de:
- Aplicar o roteiro de 5 passos para resolver problemas de programação de forma sistemática.
- Identificar o modelo IPO (Entrada, Processamento, Saída) em um enunciado.
- Escolher entre
forewhilecom base na natureza do problema. - Mapear o ciclo de vida de variáveis em laços (item vs. conjunto).
- Decompor um problema em funções com responsabilidades claras.
- Realizar o teste de mesa (ou trace manual) de um trecho de código para validar seu comportamento.
Conteúdo
Pré-requisitos
Esta aula revisa e integra o conteúdo das aulas anteriores:
- Aula 01: Introdução ao Pensamento Computacional
- Aula 03: Tipos de Dados, Variáveis e Operadores
- Aula 04: Estruturas de Decisão
- Aula 05: Modularização com Funções
- Aula 06: Funções, Bibliotecas e Depuração
- Aula 07: Estruturas de Repetição
- Aula 08: Manipulação de Strings
O problema de "cair de cabeça no código"
Uma das maiores dificuldades de quem está aprendendo a programar é começar a escrever código antes de entender o problema. O resultado costuma ser código confuso, cheio de bugs e difícil de corrigir.
Nesta aula, vamos praticar um roteiro de 5 passos que ajuda a estruturar o pensamento antes (e durante) a escrita do código. Esse roteiro amarra tudo o que vimos desde a Aula 01 — pensamento computacional, modelo IPO, decomposição — com as ferramentas das aulas seguintes.
Regra de ouro: se você não consegue explicar a solução em português, ainda não está pronto para codificar.
Passo 1 — Identifique o modelo IPO
Todo programa tem três partes: Entrada, Processamento e Saída. Antes de escrever uma linha sequer, responda:
- Entrada: quais dados o programa recebe? De onde vêm? Em que ordem?
- Processamento: qual transformação ou cálculo é necessário?
- Saída: qual é o resultado esperado? Em que formato?
Exemplo aplicado: cálculo de média de uma turma
Enunciado: Leia o número de alunos
n. Para cada aluno, leia 3 notas. Exiba a média de cada aluno e a média geral da turma.
Decomposição IPO:
| Componente | Conteúdo |
|---|---|
| Entrada | n (inteiro); depois, 3 × n notas (floats) |
| Processamento | Somar as 3 notas de cada aluno e dividir por 3; acumular todas as médias e dividir pelo total de alunos |
| Saída | n linhas com a média individual + 1 linha com a média da turma |
Perceba que ainda não escrevemos nenhum código — apenas descrevemos o fluxo.
Passo 2 — Escolha o laço
Depois de entender o IPO, identifique qual estrutura de repetição se encaixa no problema.
| Pergunta | Resposta → Ferramenta |
|---|---|
| Sei quantas vezes vou repetir? | Sim → for com range() ou sobre coleção |
| Repito até algo acontecer? | Sim → while com condição |
| Há parada antecipada por condição interna? | Sim → for + break |
| Preciso pular itens específicos? | Sim → for + continue |
| Tenho dois níveis de repetição? | Sim → laços aninhados |
Exemplos rápidos
# Caso 1: quantidade conhecida — for
for i in range(10):
print(i)
# Caso 2: repetir até o usuário digitar senha correta — while
senha = ""
while senha != "admin123":
senha = input("Senha: ")
# Caso 3: parada antecipada — for + break
for numero in range(1, 100):
if numero % 17 == 0:
print(f"Primeiro múltiplo de 17: {numero}")
break
:::tip Dica
break não transforma um for em while. O for ainda tem um teto máximo (o tamanho da coleção ou do range). Use while quando você realmente não sabe o limite superior.
:::
Passo 3 — Mapeie o ciclo de vida das variáveis
Este passo é a principal causa de bugs em iniciantes: inicializar uma variável no lugar errado.
A pergunta que decide onde inicializar é:
"Esta variável pertence a um item ou ao conjunto de todos os itens?"
| Tipo de variável | Onde inicializar | Exemplo |
|---|---|---|
| Persistente — pertence ao conjunto | Fora do laço | Total de alunos aprovados em toda a turma |
| Com reset — pertence a cada item | Dentro do laço, no início da iteração | Soma das notas de cada aluno |
| Flag booleana — registra evento num item | Dentro do laço, início da iteração | "Este aluno tirou alguma nota zero?" |
Exemplo: combinando os três tipos
num_alunos = int(input())
aprovados = 0 # persistente — pertence à TURMA
for a in range(num_alunos):
soma = 0 # reset — pertence ao ALUNO
tem_zero = False # flag — pertence ao ALUNO
for j in range(3):
nota = float(input())
soma += nota
if nota == 0:
tem_zero = True
media = soma / 3
if media >= 6 and not tem_zero:
aprovados += 1 # atualiza a persistente
print(f"Aprovados: {aprovados} de {num_alunos}")
Bug clássico — inicialização fora do lugar
# ERRADO: soma fora do laço externo
soma = 0
for a in range(num_alunos):
for j in range(3):
soma += float(input())
media = soma / 3 # 2º aluno carrega a soma do 1º!
# CERTO: soma dentro do laço externo
for a in range(num_alunos):
soma = 0 # reinicia a cada aluno
for j in range(3):
soma += float(input())
media = soma / 3
Passo 4 — Decomponha em funções
Com IPO claro, laço escolhido e variáveis mapeadas, é hora de decompor o problema em funções.
Heurística: sublinhe cada verbo diferente do enunciado. Cada verbo é candidato a virar uma função.
Exemplo: "Leia n notas, calcule a média e classifique como aprovado ou reprovado"
Dois verbos → duas funções:
def calcular_media(notas):
"""Recebe uma lista de notas. Retorna a média."""
return sum(notas) / len(notas)
def classificar(media):
"""Retorna 'Aprovado' se media >= 6, 'Reprovado' caso contrário."""
return "Aprovado" if media >= 6 else "Reprovado"
Regras de ouro para funções
- Nomes descritivos:
calcular_mediaé melhor quecalcoufn1. - Uma responsabilidade: cada função deve fazer uma coisa bem feita.
return, nãoprint: dentro de funções, prefirareturn. Oprintfica no programa principal.- Parâmetros em vez de
input(): a função recebe dados prontos — não lê do usuário. - Funções curtas: se passar de 15–20 linhas, considere quebrar em mais funções.
Por que separar leitura de cálculo? Porque a função
calcular_media(notas)pode ser testada com valores fixos (calcular_media([8, 7, 9])) — sem precisar simular entrada do usuário.
Passo 5 — Escreva o programa principal e teste
O programa principal costuma ter três blocos:
- Leitura dos dados (com laços, se necessário)
- Chamadas às funções em ordem de dependência
- Exibição dos resultados com
printe f-strings
# Bloco 1: leitura
n = int(input("Quantas notas? "))
notas = []
for _ in range(n):
notas.append(float(input()))
# Bloco 2: chamadas
media = calcular_media(notas)
situacao = classificar(media)
# Bloco 3: saída
print(f"Media: {media:.2f}")
print(f"Situacao: {situacao}")
Casos de borda que você precisa testar
Antes de considerar o programa pronto, teste:
- Entrada mínima: um único item, lista vazia (se permitido)
- Limite exato: valor na fronteira da condição (ex.: nota exatamente
6.0) - Sentinela imediato:
whilecom condição falsa logo no início - Caso de empate: dois valores iguais no cálculo do máximo
:::tip Dica
Quando o código não faz o que você esperava, use o teste de mesa (veja a próxima seção) ou adicione print de depuração com prefixo [DEBUG] para inspecionar os valores intermediários.
:::
Teste de Mesa: o rastreamento manual do código
O Teste de Mesa (ou trace manual) consiste em percorrer o código linha a linha, simulando o papel do processador com papel e caneta. É a ferramenta mais eficaz para entender a lógica interna de um algoritmo e descobrir exatamente por que um código produz um resultado inesperado.
Nessa técnica, você realiza uma execução simulada, validando o fluxo de dados e anotando o estado de cada variável em uma Tabela de Rastreio à medida que as instruções são processadas. É, essencialmente, "depurar" o código sem depender do computador.
Exemplo 1 — Simples: for com acumulador
soma = 0
for i in range(1, 5):
soma += i
print(soma)
Tabela de rastreio para cada iteração:
| Iteração | i | soma += i | soma após |
|---|---|---|---|
| 1 | 1 | 0 + 1 | 1 |
| 2 | 2 | 1 + 2 | 3 |
| 3 | 3 | 3 + 3 | 6 |
| 4 | 4 | 6 + 4 | 10 |
Saída: 10
Exemplo 2 — Mais complexo: while com sentinela e flag
n = int(input())
total = 0
quantidade = 0
tem_negativo = False
while n != 0:
if n < 0:
tem_negativo = True
total += n
quantidade += 1
n = int(input())
if quantidade > 0:
media = total / quantidade
print(f"Media: {media:.2f}")
print(f"Negativos: {tem_negativo}")
Trace para a entrada 5 / -3 / 8 / 0:
| Passo | n lido | Condição n != 0 | tem_negativo | total após | quantidade após |
|---|---|---|---|---|---|
| Leitura inicial | 5 | — | False | 0 | 0 |
| Iteração 1 | (mantém 5) | True | False | 5 | 1 |
| Releitura 1 | -3 | — | False | 5 | 1 |
| Iteração 2 | (mantém -3) | True | True | 2 | 2 |
| Releitura 2 | 8 | — | True | 2 | 2 |
| Iteração 3 | (mantém 8) | True | True | 10 | 3 |
| Releitura 3 | 0 | — | True | 10 | 3 |
| Teste do while | 0 | False → sai | True | 10 | 3 |
Cálculo final: media = 10 / 3 = 3.333...
Saída:
Media: 3.33
Negativos: True
Observação: note como a leitura inicial acontece antes do
while, e a releitura acontece no fim do corpo. Esse é o padrão dowhilecom sentinela — se você inverter a ordem, o sentinela (0) acaba sendo somado aototal.
Exemplo integrador
Vamos aplicar os 5 passos em um problema completo de revisão.
Enunciado: Leia uma frase e conte quantas palavras puras ela tem. Uma palavra é considerada "pura" se contiver apenas letras (sem dígitos). Exiba também qual é a palavra mais longa entre as puras.
Passo 1 — IPO
- Entrada: uma string (linha digitada pelo usuário)
- Processamento: dividir em palavras, verificar cada letra de cada palavra, contar palavras puras e encontrar a mais longa
- Saída: quantidade de palavras puras e a palavra mais longa
Passo 2 — Escolha do laço
- Nível externo: percorrer cada palavra da frase →
for palavra in frase.split() - Nível interno: percorrer cada caractere para verificar se é letra →
for c in palavra
→ Laços aninhados.
Passo 3 — Ciclo de vida das variáveis
| Variável | Tipo | Onde inicializar |
|---|---|---|
puras (contador) | Persistente — pertence à frase | Fora do laço externo |
mais_longa (string) | Persistente — pertence à frase | Fora do laço externo |
tem_digito (flag) | Por item — pertence à palavra | Dentro do laço externo |
Passo 4 — Decomposição em funções
Verbos do enunciado: verificar (se é pura), contar (puras), encontrar (a mais longa).
def palavra_pura(palavra):
"""Retorna True se a palavra contém apenas letras."""
for c in palavra:
if c.isdigit():
return False
return True
def analisar_frase(frase):
"""Retorna (quantidade_de_puras, palavra_mais_longa_entre_as_puras)."""
puras = 0
mais_longa = ""
for palavra in frase.split():
if palavra_pura(palavra):
puras += 1
if len(palavra) > len(mais_longa):
mais_longa = palavra
return puras, mais_longa
Passo 5 — Programa principal e teste
frase = input("Digite a frase: ")
puras, mais_longa = analisar_frase(frase)
print(f"Palavras puras: {puras}")
print(f"Mais longa: {mais_longa}")
Testes mentais:
| Entrada | puras | mais_longa |
|---|---|---|
"python e algoritmos" | 3 | "algoritmos" |
"ano2024 e semestre2" | 1 | "e" |
"abc abc" (empate) | 2 | "abc" (a primeira vence, pois só atualiza em >) |
:::note Observação sobre o caso de empate
No código acima, usamos len(palavra) > len(mais_longa). Se fosse >=, a última palavra empatada venceria. Essa é exatamente a decisão que precisa ficar clara no enunciado — e por isso o Passo 5 inclui testar casos de borda.
:::
Exercícios (checkpoints)
Para cada exercício, aplique explicitamente os 5 passos do roteiro antes de codificar. Escreva o IPO e a escolha do laço em um comentário no topo do arquivo.
Checkpoint 1 — Estatísticas de idade
Leia um inteiro n e, em seguida, n idades. Exiba a maior, a menor e a média das idades.
- Critério de verificação: para
4 / 25 / 17 / 40 / 30, a saída deve serMaior: 40 / Menor: 17 / Media: 28.00. - Dica de passo 3:
maioremenorsão variáveis persistentes — inicializá-las com a primeira idade lida evita problemas com valores "mágicos".
Checkpoint 2 — Contagem até sentinela
Leia números inteiros até o usuário digitar -1. Conte quantos são pares positivos e quantos são ímpares positivos. O -1 não entra na contagem.
- Critério de verificação: para
4 / 7 / 2 / 9 / 6 / -1, o resultado deve serPares: 3 / Impares: 2. - Dica de passo 2:
whilecom sentinela — lembre-se de ler antes do laço e reler no fim do corpo.
Checkpoint 3 — Classificador com função
Escreva uma função classificar_imc(peso, altura) que retorna uma das strings: "Abaixo", "Normal", "Sobrepeso", "Obesidade". Use a tabela:
- IMC < 18.5 →
"Abaixo" - 18.5 ≤ IMC < 25 →
"Normal" - 25 ≤ IMC < 30 →
"Sobrepeso" - IMC ≥ 30 →
"Obesidade"
Fórmula: IMC = peso / altura².
No programa principal, leia peso e altura e exiba o IMC (com 2 casas decimais) e a classificação.
- Critério de verificação: para
70 / 1.75, a saída deve serIMC: 22.86 / Classificacao: Normal.
Checkpoint 4 — Análise de texto com flag
Leia uma frase e conte quantas palavras contêm pelo menos um dígito (palavras mistas) e quantas contêm apenas letras (palavras puras). Use uma função tem_digito(palavra) que retorna um booleano.
- Critério de verificação: para
"python3 e java sao linguagens", a saída deve serPuras: 4 / Mistas: 1. - Dica de passo 3: a flag
tem_digitodeve reiniciar a cada nova palavra.
Checkpoint 5 — Teste de mesa simples
Sem executar no computador, faça o trace do código abaixo para a entrada 3 / 5 / 2 / 8. Preencha a tabela e indique a saída final.
n = int(input())
soma = 0
maior = 0
for i in range(n):
valor = int(input())
soma += valor
if valor > maior:
maior = valor
media = soma / n
print(f"Soma: {soma}")
print(f"Maior: {maior}")
print(f"Media: {media:.2f}")
| Iteração | valor lido | soma após | valor > maior? | maior após |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 |
Checkpoint 6 — Teste de mesa avançado (laço aninhado + flag + acumulador com reset)
Considere o programa abaixo, que processa n turmas com 4 notas cada. Ele calcula a média de cada turma, registra se há algum aluno reprovado (nota < 6.0) e ao final exibe a turma com maior média e quantas turmas tiveram pelo menos um reprovado.
n = int(input())
melhor_media = 0.0
melhor_turma = 0
turmas_problematicas = 0
for t in range(n):
soma = 0.0
flag_reprovado = False
for j in range(4):
nota = float(input())
soma += nota
if nota < 6.0:
flag_reprovado = True
media = soma / 4
if flag_reprovado:
turmas_problematicas += 1
if media > melhor_media:
melhor_media = media
melhor_turma = t + 1
print(f"Melhor turma: {melhor_turma}")
print(f"Media da melhor: {melhor_media:.2f}")
print(f"Turmas com reprovados: {turmas_problematicas}")
Entrada (lida de cima para baixo):
3
9.0 4.0 7.0 8.0
5.0 5.5 4.5 6.0
8.0 9.0 7.5 9.5
Preencha a tabela de trace abaixo. A coluna "fim do laço interno" é preenchida apenas na linha → fim, após as 4 notas da turma terem sido processadas. As colunas melhor_turma, melhor_media e turmas_problematicas também são atualizadas nessa linha de fechamento.
Turma 1 (t = 0, reset: soma = 0.0, flag_reprovado = False)
j | nota | soma após | flag_reprovado após |
|---|---|---|---|
| 0 | |||
| 1 | |||
| 2 | |||
| 3 | |||
| → fim | — | media = ? | — |
Após a turma 1: melhor_turma = ?, melhor_media = ?, turmas_problematicas = ?
Turma 2 (t = 1, reset: soma = 0.0, flag_reprovado = False)
j | nota | soma após | flag_reprovado após |
|---|---|---|---|
| 0 | |||
| 1 | |||
| 2 | |||
| 3 | |||
| → fim | — | media = ? | — |
Após a turma 2: melhor_turma = ?, melhor_media = ?, turmas_problematicas = ?
Turma 3 (t = 2, reset: soma = 0.0, flag_reprovado = False)
j | nota | soma após | flag_reprovado após |
|---|---|---|---|
| 0 | |||
| 1 | |||
| 2 | |||
| 3 | |||
| → fim | — | media = ? | — |
Após a turma 3: melhor_turma = ?, melhor_media = ?, turmas_problematicas = ?
Saída esperada (preencha após o trace):
Melhor turma: ?
Media da melhor: ?.??
Turmas com reprovados: ?
- Critério de verificação: ao conferir no computador, a saída deve ser
Melhor turma: 3 / Media da melhor: 8.50 / Turmas com reprovados: 2. - Questões de reflexão:
- Por que
somaeflag_reprovadoprecisam ser inicializados dentro do laço externo (for t)? - O que aconteceria se
flag_reprovado = Falsefosse movido para fora do laço externo? - Na turma 2, a nota
6.0não dispara a flag. Por quê? O que mudaria se a condição fossenota <= 6.0?
- Por que
Checkpoint 7 — Problema integrador
Escreva um programa que leia n produtos (nome e preço de cada um) e produza um relatório com:
-
Preço médio dos produtos
-
Nome do produto mais caro
-
Quantidade de produtos com preço acima da média Decomponha em pelo menos 2 funções. Use
forpara leitura e percursos. -
Critério de verificação: para
3 / arroz / 25.00 / feijao / 10.00 / carne / 60.00, a saída esperada é:
Media: 31.67
Mais caro: carne
Acima da media: 1
- Dica: você precisará de dois percursos sobre os dados — um para calcular a média (passo 1) e outro para contar quantos estão acima dela (passo 2). Guarde os dados em duas listas paralelas (
nomeseprecos) ou em uma lista de tuplas.
Resumo da Aula
Nesta aula, você praticou o roteiro de 5 passos para resolver problemas de programação:
- Identifique o modelo IPO: o que entra, o que processa, o que sai.
- Escolha o laço:
forse a quantidade é conhecida;whilese depende de condição. - Mapeie o ciclo de vida das variáveis: pertence a um item (dentro do laço) ou ao conjunto (fora)?
- Decomponha em funções: um verbo do enunciado, uma função candidata.
- Escreva o programa principal e teste: leitura → chamadas → saída + casos de borda.
Você também aprendeu a usar o teste de mesa como ferramenta de diagnóstico quando o código não produz a saída esperada.
Esses passos vão acompanhar você por toda a disciplina — e além. Sempre que um problema parecer "grande demais para começar", volte ao Passo 1.
Referências
Principais
- Python Docs — Control Flow Tools: https://docs.python.org/3/tutorial/controlflow.html
- Python Docs — Defining Functions: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
- Python Docs — String Methods: https://docs.python.org/3/library/stdtypes.html#string-methods
Aprofundamento
- Real Python — How to Think Like a Programmer: https://realpython.com/python-thinking-recursively/
- Real Python — Python Debugging with pdb: https://realpython.com/python-debugging-pdb/
- Wing, J. M. (2006). Computational Thinking. Communications of the ACM, 49(3), 33–35.