O Laço Repita / Faça-Enquanto (do-while): Repetição com Teste no Fim
Descubra o mecanismo pós-teste que garante ao menos uma execução antes de qualquer checagem lógica. Elimine a duplicação de leituras em menus interativos, construa validações de dados impenetráveis e entenda de vez o contraste entre "repetir enquanto for verdadeiro" e "repetir até que seja verdadeiro".
1. O Dilema da Primeira Execução: Por Que Precisamos de um Laço Pós-Teste?
Na aula anterior, estudamos a fundo o laço enquanto (while), cuja principal característica estrutural é o pré-teste: o processador avalia a expressão booleana antes de permitir que qualquer instrução interna seja executada. Essa salvaguarda é perfeita para situações onde é preferível não fazer nada caso a condição inicial seja falsa — por exemplo, não tentar imprimir uma lista se ela já estiver completamente vazia.
Contudo, no cotidiano do desenvolvimento de software, encontramos uma imensa classe de problemas onde o fluxo natural dos acontecimentos exige o comportamento exatamente oposto: nós precisamos agir primeiro para descobrir se precisamos agir de novo.
Imagine que você está programando o caixa eletrônico de uma agência bancária ou o menu principal de um jogo. O programa precisa, obrigatoriamente:
- Imprimir na tela as opções disponíveis (1. Consultar Saldo, 2. Sacar, 3. Transferir, 0. Sair);
- Aguardar que o usuário digite um número no teclado;
- Armazenar esse número em uma variável;
- Somente então, verificar se a opção digitada foi diferente de zero para decidir se o menu deve reaparecer ou se o programa deve encerrar.
Se tentássemos resolver esse menu utilizando exclusivamente o laço pré-teste enquanto, nos depararíamos com uma violação clássica do princípio D.R.Y (Don't Repeat Yourself). Seríamos forçados a exibir o menu e ler a opção do usuário uma primeira vez fora e antes do laço, e depois duplicar rigorosamente o mesmo trecho de código dentro do laço para as voltas subsequentes:
// Anti-padrão: leitura duplicada antes e dentro do laço
numero opcao = entrada("Escolha uma opção (1 a 3 ou 0 para sair):");
enquanto (opcao != 0) {
se (opcao == 1) {
saida("Executando módulo 1...");
} senao se (opcao == 2) {
saida("Executando módulo 2...");
}
// Leitura repetida obrigatória para evitar loop infinito:
opcao = entrada("Escolha uma opção (1 a 3 ou 0 para sair):");
}
Perceba o desperdício e o risco de manutenção: se amanhã decidirmos incluir uma quarta opção no menu, teremos que lembrar de atualizar o texto em dois pontos distintos do arquivo. Se esquecermos um deles, geraremos uma inconsistência bizarra para o usuário. É exatamente para sanar essa duplicidade que a ciência da computação concebeu os laços com teste no final (pós-teste): o faça...enquanto e o repita...até.
2. Sintaxe e Anatomia: repita...até vs faça...enquanto (do-while)
Ao estudar lógica e diferentes dialetos de programação, você notará que o laço com teste no final se apresenta sob duas roupagens principais:
- Modelo
faça...enquanto(C, C++, Java, C#, JavaScript, PHP): Conhecido universalmente comodo...while. O laço executa o bloco e continua repetindo enquanto a condição for verdadeira. - Modelo
repita...até(Pascal, Visualg / Portugol tradicional, Lua): O laço executa o bloco e continua repetindo até que a condição se torne verdadeira (ou seja, repete enquanto for falsa).
Vamos analisar a anatomia de cada uma das variações com o máximo rigor técnico.
Anatomia do Modelo faça...enquanto (do...while)
No modelo faça...enquanto, a palavra reservada faça (ou do) abre o escopo do bloco. Todas as instruções internas rodam livremente. Ao atingir o fechamento, a palavra enquanto (ou while) avalia a expressão relacional ou lógica entre parênteses. Se ela resultar em VERDADEIRO, a execução retorna imediatamente à primeira linha do corpo. Se resultar em FALSO, o laço encerra e o interpretador segue para a instrução subsequente:
// Estrutura pós-teste no logicabr.dev:
// Executa a primeira volta incondicionalmente e testa a saída no fim
numero contador = 1;
enquanto (verdadeiro) {
saida("Contador pós-teste: " + contador);
contador = contador + 1;
se (contador > 5) {
pare; // Condição de permanência/término avaliada no final
}
}
Anatomia do Modelo repita...até (repeat...until)
No Portugol clássico do ambiente acadêmico (como o Visualg) e em linguagens estruturadas históricas como o Pascal, a estrutura recebe o nome de repita ... ate que. No logicabr.dev não há palavra repita nem faca: o pós-teste é sempre escrito com enquanto (verdadeiro) e um se (condição de saída) { pare; } no fim do bloco, exatamente como no exemplo acima. Compare com o laço pré-teste convencional, que pode nem chegar a executar:
// Comparativo: laço com controle de permanência pré-teste
numero contador = 1;
enquanto (contador <= 5) {
saida("Contador convencional: " + contador);
contador = contador + 1;
}
3. A Nuance Lógica Crítica: Condição de Permanência vs Condição de Saída
Este é sem dúvida um dos tópicos que mais causa tropeços em estudantes de programação nos primeiros semestres de faculdade. Preste muita atenção na tabela de equivalência booleana abaixo:
| Estrutura | Nome da Condição | Quando Repete? | Quando Encerra? | Exemplo para Contar de 1 a 5 |
|---|---|---|---|---|
enquanto (cond) |
Condição de Permanência | Quando cond == VERDADEIRO |
Quando cond == FALSO |
enquanto (c <= 5) |
faca...enquanto (cond) |
Condição de Permanência | Quando cond == VERDADEIRO |
Quando cond == FALSO |
enquanto (c <= 5) |
enquanto (verdadeiro) + se (cond) { pare; } (logicabr.dev) |
Condição de Parada (Saída) no rodapé | Quando cond == FALSO |
Quando cond == VERDADEIRO |
se (c > 5) { pare; } |
repita...ate (cond) |
Condição de Parada (Saída) | Quando cond == FALSO |
Quando cond == VERDADEIRO |
ate (c > 5) ou ate (c == 6) |
Observe com nitidez a inversão: no faca...enquanto você declara o que você quer que continue acontecendo para manter o laço vivo. No repita...ate você declara o evento futuro que provocará o desligamento do laço.
Pela Lei da Dupla Negação e pelas Leis de De Morgan que aprendemos na Aula 07: Tabelas-Verdade, podemos converter matematicamente qualquer condição de repita...ate em uma condição de faca...enquanto aplicando o operador de negação NAO:
repita ... ate (condicao) ≡ faca ... enquanto (nao (condicao))
no logicabr.dev: enquanto (verdadeiro) { ... se (condicao) { pare; } }
Se a condição de parada for ate (nota >= 0 E nota <= 10), a sua forma equivalente no modelo enquanto será enquanto (nota < 0 OU nota > 10). Compreender essa transição economizará dezenas de horas de depuração e frustração.
4. Pré-Teste vs Pós-Teste: Matriz Comparativa Definitiva
Para que nunca reste dúvidas sobre qual estrutura selecionar ao modelar uma solução algorítmica, consulte a matriz decisória a seguir:
| Critério de Avaliação | Pré-Teste: enquanto (while) | Pós-Teste: faça...enquanto (do-while) |
|---|---|---|
| Momento do Teste | Antes de entrar no bloco de comandos. | Após a execução de todos os comandos do bloco. |
| Número Mínimo de Voltas | Zero (0). Pode não executar nenhuma vez. | Uma (1). Execução inicial incondicional. |
| Necessidade de Inicialização Prévia | Alta. A variável da condição deve ter valor válido antes do laço. | Baixa. A variável pode receber seu valor inicial dentro do próprio laço. |
| Cenário Típico de Uso | Processamento de lotes, leitura de arquivos que podem estar vazios, percurso de filas. | Menus de console, validação defensiva de inputs, jogadas repetidas por confirmação. |
| Risco de Execução Indesejada | Quase nulo se a condição inicial estiver correta. | Pode disparar efeitos colaterais na primeira volta mesmo com dados inválidos externos. |
5. Padrão Arquitetural 1: Menus Interativos de Terminal e CLI
O padrão de menu de texto é o exemplo supremo do laço com pós-teste. O algoritmo deve exibir as alternativas, colher a decisão do usuário, executar o bloco correspondente com uma estrutura escolha / caso e, caso o usuário não tenha escolhido a opção designada para término (como 0 ou 9), o ciclo é repetido sem esforço:
Veja como a implementação no logicabr.dev, com o padrão pós-teste enquanto (verdadeiro) + pare, fica cristalina, limpa e completamente livre de redundâncias:
numero opcao = -1;
enquanto (opcao != 0) {
saida("=== MENU DE OPERAÇÕES ===");
saida("1 - Emitir Relatório Financeiro");
saida("2 - Cadastrar Novo Usuário");
saida("3 - Sincronizar Banco de Dados");
saida("0 - Encerrar Sistema");
opcao = entrada("Digite o código da operação desejada:");
escolha (opcao) {
caso 1:
saida("[RELATÓRIO] Gerando demonstrativo contábil...");
pare;
caso 2:
saida("[CADASTRO] Formulário de cliente aberto.");
pare;
caso 3:
saida("[SYNC] Sincronização em nuvem finalizada.");
pare;
caso 0:
saida("Encerrando o sistema...");
pare;
padrao:
saida("[ERRO] Código de operação inválido! Tente novamente.");
}
}
saida("Programa finalizado com sucesso.");
6. Padrão Arquitetural 2: Validação Defensiva de Entrada de Dados (Sanitização)
Um dos maiores axiomas da segurança da informação e da engenharia de software afirma: "Never trust user input" (Nunca confie na entrada fornecida pelo usuário). Usuários digitam valores negativos onde deveriam digitar idades, inserem notas 15 quando a escala máxima é 10 e digitam caracteres inesperados.
O laço pós-teste é a melhor ferramenta para blindagem de inputs, pois você obriga o usuário a responder pelo menos uma vez e, caso ele insira um valor fora do domínio de integridade permitido, o laço simplesmente rejeita a resposta e solicita a digitação novamente:
numero nota = -1;
enquanto (verdadeiro) {
nota = entrada("Informe a nota final do aluno (de 0.0 a 10.0):");
se (nota < 0.0 ou nota > 10.0) {
saida("Nota inválida! A pontuação deve estar restrita entre 0 e 10.");
} senao {
pare; // Nota validada com sucesso! Sai do loop.
}
}
saida("Nota homologada com êxito: " + nota + " pontos.");
Observe a clareza deste padrão: a variável nota não precisou ser inicializada artificialmente com nenhum valor fictício (como -999) antes de entrar no laço. Ela é criada vazia, recebe seu valor genuíno na primeira linha dentro do laço, e o teste no rodapé só permite que a execução atravesse o portão se a nota for estritamente válida.
7. Padrão Arquitetural 3: Repetição por Confirmação ("Deseja Continuar?")
Outra aplicação clássica de sistemas corporativos é o processamento de cálculos contábeis sob demanda, em que o operador realiza o cálculo para um cliente e o sistema indaga: "Deseja realizar outro cálculo para um novo cliente? (S/N)".
moeda faturamentoTotal = 0.0;
texto resposta = "S";
enquanto (resposta == "S" ou resposta == "s") {
moeda valorVenda = entrada("Digite o valor da venda (R$):");
faturamentoTotal = faturamentoTotal + valorVenda;
resposta = entrada("Venda registrada! Deseja lançar outra venda? (S para Sim / N para Não):");
}
saida("Faturamento total consolidado no expediente: R$ " + faturamentoTotal);
8. O Que Acontece no Hardware: Por Que o do-while É Tão Rápido?
Em nível de microarquitetura de processadores e compiladores modernos, o laço do-while possui uma elegância mecânica fascinante quando comparado ao while tradicional.
Em um laço while clássico, a CPU precisa executar um salto incondicional (jmp) logo na largada para alcançar a rotina de checagem, ou precisa realizar um teste prévio que pode sofrer falha de predição de desvio (branch misprediction) na primeira rodada.
No laço do-while, o código Assembly gerado pelo compilador é perfeitamente linear e contíguo:
- As instruções do corpo do laço começam exatamente no endereço sequencial atual da memória de código. A CPU apenas segue avançando o Program Counter (PC);
- Ao término do bloco, uma única instrução de comparação (
CMP) é executada; - Imediatamente em seguida, um salto condicional (por exemplo,
JNE loop_start) transfere o fluxo de volta caso a condição seja atendida.
Essa disposição economiza ciclos de clock preciosos e aproveita ao máximo os circuitos de Branch Prediction da CPU: processadores modernos aprendem rapidamente que loops tendem a se repetir, mantendo as instruções já decodificadas diretamente na Loop Stream Detector (LSD) e no cache de micro-operações (uOp Cache).
9. Panorama Multi-Linguagem: C, Java, JavaScript, C# e o Truque do Python
A sintaxe do laço com pós-teste é praticamente universal entre as principais linguagens da indústria, com uma notória e curiosa exceção: a linguagem Python.
// Todas as linguagens derivadas do C compartilham exatamente a mesma sintaxe:
int tentativas = 0;
do {
tentativas++;
printf("Tentativa %d executada.\n", tentativas);
} while (tentativas < 3);
Atenção ao Ponto e Vírgula (;): Observe que na linguagem C, Java, C# e C++, o comando do...while é uma das raríssimas estruturas de controle de bloco que exige obrigatoriamente um ponto e vírgula no final logo após os parênteses da condição: while (condicao);. Esquecer esse ponto e vírgula em C ou Java resulta em erro de compilação imediato!
E como fazer um do-while em Python?
A linguagem Python propositalmente não possui a palavra-chave do. O filósofo e criador do Python, Guido van Rossum, decidiu que adicionar uma estrutura separada violaria o princípio de simplicidade do Zen of Python: "There should be one-- and preferably only one --obvious way to do it".
Para obter exatamente o mesmo comportamento de um pós-teste em Python, a convenção padrão recomendada pela comunidade mundial é utilizar um laço infinito while True: e colocar uma verificação de saída com a palavra-chave break ao final do bloco:
# Equivalente em Python ao do-while com teste no fim:
while True:
opcao = int(input("Digite um número de 1 a 5: "))
# Condição de saída avaliada no fim do corpo:
if 1 <= opcao <= 5:
break # Sai do laço se o dado for válido
print("Entrada inválida! Digite novamente.")
print(f"Valor aceito: {opcao}")
10. 5 Armadilhas Críticas com Laços Pós-Teste e Como Evitá-las
Apesar da sua grande utilidade prática, o laço com teste no final esconde algumas armadilhas sutis. Fique alerta aos 5 cenários mais comuns:
-
O Ponto e Vírgula Esquecido na Família C: Conforme alertamos, em linguagens como C, C++ e Java, escrever
do { ... } while (x < 10)sem o;final é um dos erros sintáticos mais frequentes de calouros. - Efeitos Colaterais Indesejados na 1ª Volta: Como o bloco roda incondicionalmente, se você tiver uma rotina que cobra uma taxa bancária, dispara um e-mail ou limpa uma tabela de banco de dados na primeira linha, essa ação ocorrerá mesmo que as premissas externas do sistema não tenham sido cumpridas!
-
Inversão de Lógica entre "Repita Até" e "Faça Enquanto": Se você migrar um algoritmo escrito no Visualg com
ate (x == 0)para C ou JavaScript e simplesmente escreverwhile (x == 0), o programa quebrará de imediato. Você precisa inverter a condição:while (x != 0). -
Escopo de Variáveis em C, Java e C#: Variáveis declaradas dentro das chaves
do { int escolha = 1; } while (escolha != 0);não são visíveis para a expressão dowhile! A variável testada no rodapé precisa obrigatoriamente ser declarada no escopo externo que antecede odo. -
Loops Infinitos por Falha de Atualização: Assim como no
while, se a condição pós-teste depender de uma variável que não é alterada pelas instruções internas, o computador rodará o bloco eternamente consumindo 100% de uso de CPU.
11. Projeto Prático Resolvido: Sistema de Caixa Eletrônico CaixaBank CLI
Vamos integrar todos os conceitos aprendidos — laço pós-teste faça...enquanto, validação rigorosa de saques, acumuladores de saldo e estrutura de seleção escolha / caso — em um sistema de terminal completo simulando um terminal bancário 24 horas:
moeda saldo = 1250.00; // Saldo inicial da conta do cliente
numero opcao = -1;
saida("=== BEM-VINDO AO TERMINAL 24 HORAS CAIXABANK ===");
enquanto (opcao != 0) {
saida("----------------------------------------");
saida("1 - Consultar Saldo Disponível");
saida("2 - Realizar Depósito em Dinheiro");
saida("3 - Efetuar Saque Eletrônico");
saida("0 - Encerrar Atendimento e Devolver Cartão");
opcao = entrada("Escolha a opção desejada:");
escolha (opcao) {
caso 1:
saida("[CONSULTA] Saldo atual em conta: R$ " + saldo);
pare;
caso 2:
moeda valorDeposito = entrada("Informe o valor para depósito (R$):");
se (valorDeposito <= 0.0) {
saida("Valor inválido! O depósito deve ser maior que zero.");
} senao {
saldo = saldo + valorDeposito;
saida("[SUCESSO] Depósito de R$ " + valorDeposito + " processado com sucesso!");
saida("Novo saldo disponível: R$ " + saldo);
}
pare;
caso 3:
moeda valorSaque = entrada("Informe o valor para saque (R$):");
se (valorSaque <= 0.0) {
saida("Valor de saque inválido!");
} senao se (valorSaque > saldo) {
saida("[ERRO] Saldo insuficiente! Seu saldo atual é de R$ " + saldo);
} senao {
saldo = saldo - valorSaque;
saida("[SAQUE] Saque aprovado! Retire as cédulas na gaveta inferior.");
saida("Saldo restante: R$ " + saldo);
}
pare;
caso 0:
saida("Obrigado por utilizar os serviços CaixaBank. Retire seu cartão!");
pare;
padrao:
saida("Opção inexistente no menu! Por favor, tente novamente.");
}
}
saida("Sessão encerrada com segurança.");
12. Perguntas Frequentes (FAQ)
Quando devo escolher 'enquanto' e quando devo escolher 'faça-enquanto'?
A regra de ouro é simples: se a primeira iteração depende de uma condição que pode não ter sido atingida (como processar arquivos de uma pasta que pode estar vazia), utilize o enquanto (pré-teste). Se a primeira iteração precisa obrigatoriamente acontecer para que o próprio dado da condição seja gerado ou capturado (como menus, perguntas de confirmação e validação de digitação), escolha o faça-enquanto (pós-teste).
É possível usar comandos aninhados de 'faça-enquanto' dentro de outro laço?
Sim, perfeitamente! Como demonstramos no exercício do caixa eletrônico, o laço principal do menu contém outro faça-enquanto secundário no caso 2 dedicado exclusivamente a forçar o usuário a digitar um valor positivo de depósito. Laços aninhados de teste no fim são a espinha dorsal de validações robustas.
O que acontece se a condição do 'faça-enquanto' for falsa logo na primeira checagem?
O corpo do laço terá sido executado exatamente uma única vez. Ao avaliar a condição pela primeira vez no fim da rodada e encontrar o valor lógico FALSO, a CPU cancela novos saltos de repetição e dá seguimento à próxima linha fora do laço.
Por que algumas pessoas dizem que o laço 'repita...até' é mais intuitivo na linguagem falada?
Na linguagem natural dos seres humanos, frequentemente usamos metas de término: "Mexa o bolo até que a massa fique homogênea" ou "Caminhe até alcançar a esquina". O comando repita...ate modela fielmente essa intuição linguística, declarando a condição de chegada ao invés da condição de permanência.
Qual o impacto de desempenho entre pré-teste e pós-teste?
Em aplicações do dia a dia, a diferença é infinitesimal (nanossegundos). Contudo, em computação de alto desempenho, processamento gráfico e motores de jogos (game engines), o do-while costuma apresentar ligeira vantagem de throughput por ter seu corpo alinhado diretamente na sequência do fluxo de controle, reduzindo saltos desnecessários na primeira iteração.
Experimente o Laço Repita / Faça-Enquanto na Prática!
Abra o editor online do logicabr.dev, crie menus interativos e teste a validação de dados em tempo real no console interativo.
Abrir IDE Online e Experimentar →