Pular para o conteúdo principal

Revisão: Fundamentos

Objetivos de Aprendizagem

Ao final desta aula, você deve ser capaz de:

  1. Aplicar o roteiro de 5 passos para resolver problemas de programação de forma sistemática.
  2. Identificar o modelo IPO (Entrada, Processamento, Saída) em um enunciado.
  3. Escolher entre for e while com base na natureza do problema.
  4. Mapear o ciclo de vida de variáveis em laços (item vs. conjunto).
  5. Decompor um problema em funções com responsabilidades claras.
  6. 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:


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:

ComponenteConteúdo
Entradan (inteiro); depois, 3 × n notas (floats)
ProcessamentoSomar as 3 notas de cada aluno e dividir por 3; acumular todas as médias e dividir pelo total de alunos
Saídan 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.

PerguntaResposta → Ferramenta
Sei quantas vezes vou repetir?Simfor com range() ou sobre coleção
Repito até algo acontecer?Simwhile com condição
Há parada antecipada por condição interna?Simfor + break
Preciso pular itens específicos?Simfor + 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ávelOnde inicializarExemplo
Persistente — pertence ao conjuntoFora do laçoTotal de alunos aprovados em toda a turma
Com reset — pertence a cada itemDentro do laço, no início da iteraçãoSoma das notas de cada aluno
Flag booleana — registra evento num itemDentro 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

  1. Nomes descritivos: calcular_media é melhor que calc ou fn1.
  2. Uma responsabilidade: cada função deve fazer uma coisa bem feita.
  3. return, não print: dentro de funções, prefira return. O print fica no programa principal.
  4. Parâmetros em vez de input(): a função recebe dados prontos — não lê do usuário.
  5. 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:

  1. Leitura dos dados (com laços, se necessário)
  2. Chamadas às funções em ordem de dependência
  3. Exibição dos resultados com print e 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: while com 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çãoisoma += isoma após
110 + 11
221 + 23
333 + 36
446 + 410

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:

Passon lidoCondição n != 0tem_negativototal apósquantidade após
Leitura inicial5False00
Iteração 1(mantém 5)TrueFalse51
Releitura 1-3False51
Iteração 2(mantém -3)TrueTrue22
Releitura 28True22
Iteração 3(mantém 8)TrueTrue103
Releitura 30True103
Teste do while0False → saiTrue103

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 do while com sentinela — se você inverter a ordem, o sentinela (0) acaba sendo somado ao total.


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ávelTipoOnde inicializar
puras (contador)Persistente — pertence à fraseFora do laço externo
mais_longa (string)Persistente — pertence à fraseFora do laço externo
tem_digito (flag)Por item — pertence à palavraDentro 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:

Entradapurasmais_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 ser Maior: 40 / Menor: 17 / Media: 28.00.
  • Dica de passo 3: maior e menor sã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 ser Pares: 3 / Impares: 2.
  • Dica de passo 2: while com 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 ser IMC: 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 ser Puras: 4 / Mistas: 1.
  • Dica de passo 3: a flag tem_digito deve 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çãovalor lidosoma apósvalor > 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)

jnotasoma apósflag_reprovado após
0
1
2
3
→ fimmedia = ?

Após a turma 1: melhor_turma = ?, melhor_media = ?, turmas_problematicas = ?

Turma 2 (t = 1, reset: soma = 0.0, flag_reprovado = False)

jnotasoma apósflag_reprovado após
0
1
2
3
→ fimmedia = ?

Após a turma 2: melhor_turma = ?, melhor_media = ?, turmas_problematicas = ?

Turma 3 (t = 2, reset: soma = 0.0, flag_reprovado = False)

jnotasoma apósflag_reprovado após
0
1
2
3
→ fimmedia = ?

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:
    1. Por que soma e flag_reprovado precisam ser inicializados dentro do laço externo (for t)?
    2. O que aconteceria se flag_reprovado = False fosse movido para fora do laço externo?
    3. Na turma 2, a nota 6.0 não dispara a flag. Por quê? O que mudaria se a condição fosse nota <= 6.0?

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 for para 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 (nomes e precos) ou em uma lista de tuplas.

Resumo da Aula

Nesta aula, você praticou o roteiro de 5 passos para resolver problemas de programação:

  1. Identifique o modelo IPO: o que entra, o que processa, o que sai.
  2. Escolha o laço: for se a quantidade é conhecida; while se depende de condição.
  3. Mapeie o ciclo de vida das variáveis: pertence a um item (dentro do laço) ou ao conjunto (fora)?
  4. Decomponha em funções: um verbo do enunciado, uma função candidata.
  5. 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

Aprofundamento