ENTENDA A IDEIA
A ideia central
Pense nas formas de repetir uma tarefa na vida real. "Dê 10 voltas na pista" — você sabe de antemão quantas vezes. "Bata as claras até o ponto de neve" — você repete até uma condição, sem saber quantas batidas serão. "Fique atento ao rádio" — repetição sem fim previsto, até alguém mandar parar. E "para cada encomenda na esteira, cole uma etiqueta" — repetição dirigida por uma coleção de itens.
ENTENDA A IDEIA
for: o único loop de Go e todas as suas formas · parte 2
A maioria das linguagens dá uma palavra-chave para cada uma dessas formas: while, do-while, for clássico, for-in, for-of, forEach... JavaScript tem meia dúzia; Python tem for e while mais compreensões. Go olhou para esse cardápio e aplicou a filosofia que você já conhece das aulas anteriores: uma coisa, um jeito. Existe uma única palavra de repetição em Go — for — e ela assume todas as quatro formas. A pergunta "qual tipo de loop eu uso?" simplesmente não existe; sobra a pergunta que importa: "qual é a condição de parada?".
ENTENDA A IDEIA
for: o único loop de Go e todas as suas formas · parte 3
Essa pergunta — a condição de parada — é o coração conceitual desta aula, e merece um minuto de respeito. Todo loop é um contrato com três cláusulas: de onde parte o estado, o que muda a cada volta e o que faz a repetição terminar. Quando qualquer uma das três está errada, você tem um dos dois bugs clássicos de repetição: o off-by-one (uma volta a mais ou a menos — de longe o erro mais cometido em loops, em qualquer linguagem) e o loop infinito (a condição de parada nunca chega). Você vai encontrar os dois nesta aula, de propósito e em ambiente controlado: aqui no curso, um loop infinito é interrompido pelo runner após 10 segundos com uma mensagem amigável — em produção, ele é uma CPU a 100% e um servidor que parou de responder.
O for completo de Go organiza as três cláusulas em uma linha — inicialização, condição e pós-passo, separadas por ponto e vírgula, como você verá no código anotado. Tire a primeira e a terceira cláusula e sobra o for só com condição: o "while" de Go. Tire tudo e sobra o for de chaves vazias: o loop infinito deliberado, que é uma ferramenta legítima (servidores vivem em um) quando existe um break planejado. E a quarta forma, for range, percorre coleções — você vai usá-la de verdade quando slices e maps chegarem no próximo módulo; hoje ela aparece na sua forma mais simples, range sobre um número, que repete n vezes sem cerimônia.
Uma nota de ergonomia antes do código: as chaves são obrigatórias mesmo para corpo de uma linha, e os parênteses em volta das cláusulas são proibidos — escrever o cabeçalho do for entre parênteses, ao estilo C ou JavaScript, não compila. É o gofmt mental da linguagem: menos variações visuais, menos discussão.
CÓDIGO ANOTADO
Código anotado · parte 1
Um programa, quatro formas de for. Cada bloco resolve um problema pequeno e real, e os comentários marcam qual forma está em uso.
A forma completa: três cláusulas separadas por ponto e vírgula. A inicialização lap := 1 roda uma vez; a condição (lap até 3, inclusive) é testada antes de cada volta; o pós-passo lap++ roda depois de cada corpo. A variável lap só existe dentro do loop — escopo de bloco, como no if com init que você verá na próxima aula.
package main
import "fmt"
func main() {
// forma completa: contagem conhecida
for lap := 1; lap <= 3; lap++ {
fmt.Println("volta", lap)
} CÓDIGO ANOTADO
Código anotado · parte 2
A forma "while": só a condição. O estado (balance) vive fora do loop e é modificado no corpo. Enquanto a condição segurar, o corpo repete — a pergunta "quantas voltas?" não tem resposta prévia; depende dos dados.
// forma while: repete até a condição falhar
balance := 100
for balance > 0 {
balance -= 35
fmt.Println("saldo restante:", balance)
} CÓDIGO ANOTADO
Código anotado · parte 3
A forma infinita com break: for sem cláusula alguma repete para sempre, e o break é a única saída. O par for {} + break interno é o esqueleto de todo servidor, worker e leitor de fila — o loop não sabe quando vai parar, mas sabe o que o faz parar. O continue, mostrado junto, pula direto para a próxima volta sem executar o resto do corpo.
// forma infinita + break e continue
attempts := 0
for {
attempts++
if attempts%2 == 0 {
continue // pula os pares
}
fmt.Println("tentativa ímpar:", attempts)
if attempts >= 5 {
break
}
} CÓDIGO ANOTADO
Código anotado · parte 4
E a forma range, aqui na variação mais enxuta: range n sobre um inteiro repete n vezes, entregando o contador de 0 a n-1. É a forma idiomática moderna para "faça n vezes" quando você não precisa controlar passo nem direção. As outras variações de range — sobre slices, maps e strings — são o assunto natural do módulo de tipos compostos.
// forma range: n repetições, contador automático
for count := range 3 {
fmt.Println("range entregou:", count)
}
fmt.Println("fim")
} PASSO A PASSO
Veja a ideia em movimento
Avance pelos passos e observe como o estado muda a cada decisão.
EXPERIMENTE
Experimente com o código
O programa abaixo tem um contador regressivo. Preveja: quantas linhas imprime, e qual é a última?
Agora três experiências, uma de cada vez. Primeira: na condição do contador regressivo, troque "maior que zero" por "maior ou igual a zero" e veja o off-by-one aparecer — uma linha a mais. Segunda: no segundo loop, troque continue por break e explique a diferença na soma antes de conferir. Terceira, a mais importante: troque countdown-- por countdown++ e rode. O loop nunca termina; o runner o mata em 10 segundos com a mensagem de tempo excedido. Essa é a cara de um loop infinito acidental — condição que nunca vira falsa porque o passo anda na direção errada. Conhecê-la aqui, onde custa 10 segundos, é o objetivo.
DESAFIO
Some um intervalo
Some um intervalo
Implemente SumBetween(from int, to int) int, que soma todos os inteiros de from a to, incluindo os dois extremos. Se from for maior que to, o intervalo é vazio: devolva 0.
Entrada: dois inteiros quaisquer (podem ser negativos). Saída: a soma do intervalo fechado.
Exemplos:
SumBetween(1, 4)→10— soma de 1+2+3+4;SumBetween(5, 5)→5— intervalo de um único valor;SumBetween(-3, 3)→0— negativos cancelam positivos;SumBetween(10, 1)→0— intervalo vazio.
Os casos 2 e 4 cercam os limites: um intervalo que é um ponto só, e um que não existe. Pense em como a condição do for trata cada um antes de escrever qualquer if.
Dica 1
Um acumulador começa em 0 antes do loop; o loop percorre de from até to somando cada valor nele.
Dica 2
A condição precisa ser value `<=` to (com igual) para incluir o extremo — e o caso from > to se resolve sozinho: a condição já nasce falsa.
A solução aparece depois do acerto ou de 3 tentativas.
Solução e explicação
package main
// SumBetween soma todos os inteiros de from até to, inclusive os extremos.
func SumBetween(from int, to int) int {
sum := 0
for value := from; value <= to; value++ {
sum += value
}
return sum
}
Ver harness de testes somente leitura
package main
import (
"encoding/json"
"fmt"
"time"
)
type caseResult struct {
Case int `json:"case"`
Pass bool `json:"pass"`
Input string `json:"input,omitempty"`
Want string `json:"want,omitempty"`
Got string `json:"got,omitempty"`
Ms int64 `json:"ms,omitempty"`
}
func main() {
cases := []struct {
from, to int
want int
}{
{1, 4, 10},
{5, 5, 5},
{1, 100, 5050},
{-3, 3, 0},
{10, 1, 0},
}
for index, test := range cases {
started := time.Now()
got := SumBetween(test.from, test.to)
result := caseResult{
Case: index + 1, Pass: got == test.want,
Input: fmt.Sprintf("%d, %d", test.from, test.to),
Want: fmt.Sprintf("%d", test.want), Got: fmt.Sprintf("%d", got),
Ms: time.Since(started).Milliseconds(),
}
line, _ := json.Marshal(result)
fmt.Printf("RESULT %s\n", line)
}
}
DESAFIO
Implemente a potência por repetição
Implemente a potência por repetição
Implemente Pow(base int, exponent int) int, que calcula base elevado a exponent para expoentes não negativos — sem usar math.Pow.
Entrada: uma base qualquer e um expoente não negativo. Saída: a potência inteira.
Exemplos:
Pow(2, 10)→1024;Pow(5, 0)→1— qualquer base elevada a zero é 1;Pow(0, 3)→0;Pow(-2, 3)→-8— bases negativas funcionam sem tratamento especial.
A diferença estrutural para o exercício 1: o acumulador aqui é multiplicativo, e isso muda o valor inicial. Se Pow(5, 0) está devolvendo 0, você inicializou errado — pense em qual valor "não interfere" numa multiplicação.
Dica 1
Potência é multiplicação repetida: exponent vezes, multiplique um acumulador por base.
Dica 2
O acumulador começa em 1, não em 0 — e é isso que faz Pow(x, 0) devolver 1 sem caso especial.
A solução aparece depois do acerto ou de 3 tentativas.
Solução e explicação
package main
// Pow calcula base elevado a exponent para expoentes não negativos.
func Pow(base int, exponent int) int {
result := 1
for count := 0; count < exponent; count++ {
result *= base
}
return result
}
Ver harness de testes somente leitura
package main
import (
"encoding/json"
"fmt"
"time"
)
type caseResult struct {
Case int `json:"case"`
Pass bool `json:"pass"`
Input string `json:"input,omitempty"`
Want string `json:"want,omitempty"`
Got string `json:"got,omitempty"`
Ms int64 `json:"ms,omitempty"`
}
func main() {
cases := []struct {
base, exponent int
want int
}{
{2, 10, 1024},
{5, 0, 1},
{0, 3, 0},
{3, 4, 81},
{-2, 3, -8},
}
for index, test := range cases {
started := time.Now()
got := Pow(test.base, test.exponent)
result := caseResult{
Case: index + 1, Pass: got == test.want,
Input: fmt.Sprintf("%d, %d", test.base, test.exponent),
Want: fmt.Sprintf("%d", test.want), Got: fmt.Sprintf("%d", got),
Ms: time.Since(started).Milliseconds(),
}
line, _ := json.Marshal(result)
fmt.Printf("RESULT %s\n", line)
}
}
DESAFIO
Conte os passos de Collatz
Conte os passos de Collatz
A conjectura de Collatz é uma brincadeira matemática famosa: partindo de qualquer inteiro positivo, se ele é par divida por 2, se é ímpar calcule 3n+1, e repita — a sequência sempre parece chegar a 1, embora ninguém tenha provado. Implemente CollatzSteps(n int) int, que conta quantos passos a sequência leva de n até 1. Para n menor que 1, devolva -1.
Entrada: um inteiro. Saída: o número de transformações até chegar a 1, ou -1 para entrada inválida.
Exemplos:
CollatzSteps(6)→8— a rota é 6→3→10→5→16→8→4→2→1;CollatzSteps(1)→0— já começa em 1, nenhum passo;CollatzSteps(27)→111— algumas rotas são surpreendentemente longas;CollatzSteps(0)→-1.
Aqui está o motivo de este exercício fechar a aula: você não sabe quantas voltas o loop dará — nem tem como saber; essa é literalmente a conjectura. A forma completa do for não serve; escolha a forma certa. E atenção redobrada com a validação: sem ela, a entrada 0 produz um loop genuinamente infinito.
Dica 1
Aqui o número de voltas não é conhecido de antemão: use a forma while do for — for n != 1 — com um contador que incrementa a cada transformação.
Dica 2
Par ou ímpar se decide com n%2 == 0; valide n < 1 antes do loop para não rodar para sempre com entrada inválida.
A solução aparece depois do acerto ou de 3 tentativas.
Solução e explicação
package main
// CollatzSteps conta quantos passos a sequência de Collatz leva de n até 1.
func CollatzSteps(n int) int {
if n < 1 {
return -1
}
steps := 0
for n != 1 {
if n%2 == 0 {
n = n / 2
} else {
n = 3*n + 1
}
steps++
}
return steps
}
Ver harness de testes somente leitura
package main
import (
"encoding/json"
"fmt"
"time"
)
type caseResult struct {
Case int `json:"case"`
Pass bool `json:"pass"`
Input string `json:"input,omitempty"`
Want string `json:"want,omitempty"`
Got string `json:"got,omitempty"`
Ms int64 `json:"ms,omitempty"`
}
func main() {
cases := []struct {
n int
want int
}{
{1, 0},
{6, 8},
{27, 111},
{2, 1},
{0, -1},
}
for index, test := range cases {
started := time.Now()
got := CollatzSteps(test.n)
result := caseResult{
Case: index + 1, Pass: got == test.want,
Input: fmt.Sprintf("%d", test.n),
Want: fmt.Sprintf("%d", test.want), Got: fmt.Sprintf("%d", got),
Ms: time.Since(started).Milliseconds(),
}
line, _ := json.Marshal(result)
fmt.Printf("RESULT %s\n", line)
}
}
VOCÊ CHEGOU AO RESUMO
O que você leva desta aula
- Go tem um loop:
forcompleto (init; condição; pós),forcondição (o while),for {}(infinito, combreakplanejado) efor range(coleções erange npara "n vezes"). - A condição é testada antes de cada volta — um loop pode executar zero vezes, e é assim que intervalos vazios se resolvem sem
if. - Acumuladores começam no elemento neutro da operação: 0 para soma, 1 para multiplicação — e casos "especiais" como expoente zero deixam de ser especiais.
breaksai do loop;continuepula para a próxima volta; off-by-one se evita re-derivando a condição (estrita ou inclusiva) em vez de copiá-la de outro loop.- Loop infinito acidental = condição de parada inalcançável; valide as entradas que quebrariam a prova de parada — aqui custa um timeout de 10s, em produção custa um servidor.
Na próxima aula, imersao/07-if-else, o outro pilar do controle de fluxo — incluindo o init statement (if err := f(); err != nil) que é a assinatura visual mais reconhecível de todo código Go.