Sua dose diária de programação

Conceito, 3 exemplos e 3 exercícios: uma pequena conquista de cada vez.

✦   Lição

Decisões com condicionais

Um programa precisa tomar decisões: confirmar o pedido só se houver estoque, aprovar o aluno só com nota e frequência suficientes. Em Go, isso é feito com if: if condição { ... } roda o bloco quando a condição é true, e o else opcional roda quando ela é false. A condição é sempre um bool, como estoque > 0 ou pago == true; Go não aceita número ou texto no lugar dela, e não usa parênteses em volta da condição, mas as chaves são obrigatórias.

Go Fundamentos Iniciante 3 exemplos + 3 exercícios
42 lições publicadas 10 linguagens novas lições todo dia útil
Go / Fundamentos

Decisões com condicionais

por Ellery Blackwood
Iniciante

Entenda o conceito

Um programa precisa tomar decisões: confirmar o pedido só se houver estoque, aprovar o aluno só com nota e frequência suficientes. Em Go, isso é feito com if: if condição { ... } roda o bloco quando a condição é true, e o else opcional roda quando ela é false. A condição é sempre um bool, como estoque > 0 ou pago == true; Go não aceita número ou texto no lugar dela, e não usa parênteses em volta da condição, mas as chaves são obrigatórias.

Para mais de dois caminhos, encadeie else if. As condições são testadas de cima para baixo e só o bloco da primeira verdadeira roda; por isso a ordem importa (teste >= 900 antes de >= 600). Condições compostas usam && (e), || (ou) e ! (não), e o Go para de avaliar assim que já sabe o resultado.

Um recurso típico do Go é o if com instrução curta: if n, err := strconv.Atoi(texto); err != nil { ... }. A parte antes do ; roda primeiro, e as variáveis criadas ali só existem dentro desse if e dos seus else. É o jeito idiomático de tratar erros e cálculos de uso único sem espalhar variáveis pela função.

↳ Pontos-chave
  • if condição { ... } else { ... }: sem parênteses na condição, com chaves obrigatórias e o else na mesma linha do }.
  • A condição precisa ser bool; if estoque { ... } com um int não compila, escreva if estoque > 0.
  • Em uma cadeia de else if, só roda o bloco da primeira condição verdadeira; ordene das faixas mais restritas para as mais amplas.
  • &&, || e ! combinam condições; a avaliação para assim que o resultado é conhecido (curto-circuito).
  • if v, err := f(); err != nil { ... }: as variáveis da instrução curta só existem dentro do if e dos seus else.

Do conceito à prática

3 exemplos para ver a ideia funcionando.

01Estoque da loja com if e else

estoque.go
package main

import "fmt"

func main() {
	produto := "Fone de ouvido"
	estoque := 5
	pedido := 3

	// if/else: um dos dois blocos sempre roda.
	if pedido <= estoque {
		estoque = estoque - pedido
		fmt.Printf("Pedido confirmado: %d x %s.\n", pedido, produto)
	} else {
		fmt.Printf("Só temos %d unidades de %s.\n", estoque, produto)
	}

	// if sozinho: o bloco roda só quando a condição é true.
	if estoque < 3 {
		fmt.Println("Atenção: estoque baixo, hora de repor.")
	}

	// A condição pode ser uma variável bool.
	esgotado := estoque == 0
	if esgotado {
		fmt.Println("Produto esgotado.")
	} else {
		fmt.Println("Restam", estoque, "unidades.")
	}
}

O primeiro if tem dois caminhos: se o pedido cabe no estoque, a venda é confirmada e o estoque diminui; senão, o else avisa quantas unidades existem. Exatamente um dos dois blocos roda. Logo depois, um if sem else só imprime o alerta quando o estoque fica abaixo de 3.

No fim, o resultado da comparação estoque == 0 é guardado na variável esgotado, do tipo bool, e usado direto como condição. Dar nome à condição deixa o if mais fácil de ler, principalmente quando a regra cresce.

02Medalhas do torneio com else if, && e ||

medalhas.go
package main

import "fmt"

// medalha devolve a premiação de acordo com os pontos.
// As faixas são testadas de cima para baixo: a primeira verdadeira vence.
func medalha(pontos int) string {
	if pontos >= 900 {
		return "ouro"
	} else if pontos >= 600 {
		return "prata"
	} else if pontos >= 300 {
		return "bronze"
	} else {
		return "participação"
	}
}

func main() {
	fmt.Println("Resultado do torneio:")
	fmt.Printf("%-6s %4d pontos -> %s\n", "Bia", 950, medalha(950))
	fmt.Printf("%-6s %4d pontos -> %s\n", "Caio", 600, medalha(600))
	fmt.Printf("%-6s %4d pontos -> %s\n", "Duda", 420, medalha(420))
	fmt.Printf("%-6s %4d pontos -> %s\n", "Enzo", 120, medalha(120))

	// && exige as duas condições; || aceita qualquer uma delas.
	vidas := 2
	fases := 8
	if fases >= 8 && vidas > 0 {
		fmt.Println("Bia liberou a fase secreta!")
	}
	if vidas == 0 || fases < 3 {
		fmt.Println("Fim de jogo.")
	} else {
		fmt.Println("O jogo continua.")
	}
}

A função medalha recebe os pontos e devolve a premiação. As faixas são testadas na ordem: com 950 pontos, pontos >= 900 já é verdadeira e as outras nem são avaliadas. Com 600, a primeira falha e a segunda acerta. Se a ordem fosse invertida (>= 300 primeiro), todo mundo acima de 300 ganharia bronze.

No main, && exige que as duas condições sejam verdadeiras para liberar a fase secreta, e || encerra o jogo se qualquer uma delas for verdadeira. Como nenhuma é, o else informa que o jogo continua.

03Renovação na biblioteca com if de instrução curta

renovacao.go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	// Dados como chegam de um formulário: tudo em texto.
	codigoDigitado := "1042"
	diasAtraso := 0
	renovacoes := 1
	reservado := false

	// if com instrução curta: n e err só existem dentro deste if/else.
	if n, err := strconv.Atoi(codigoDigitado); err != nil {
		fmt.Println("Código de leitor inválido:", codigoDigitado)
	} else {
		fmt.Println("Leitor encontrado:", n)
	}

	// Condição composta: && exige todas as partes e ! inverte o bool.
	podeRenovar := diasAtraso == 0 && renovacoes < 2 && !reservado
	if podeRenovar {
		fmt.Println("Renovação aprovada por mais 14 dias.")
	} else if reservado {
		fmt.Println("Livro reservado por outro leitor.")
	} else {
		fmt.Println("Renovação negada: devolva o livro na biblioteca.")
	}

	// A instrução curta também serve para um cálculo de uso único.
	if multa := float64(diasAtraso) * 0.75; multa > 0 {
		fmt.Printf("Multa a pagar: R$ %.2f\n", multa)
	} else {
		fmt.Println("Nenhuma multa pendente.")
	}
}

O strconv.Atoi converte texto em inteiro e devolve dois valores: o número e um erro. Com a instrução curta, n e err nascem dentro do if e só existem nele e no else; fora dali, o nome n nem é conhecido. É assim que código Go costuma tratar erros: testa err != nil logo depois da chamada.

A regra de renovação junta três condições com && e usa !reservado para inverter o bool. O último if mostra a instrução curta num cálculo: a variável multa só é útil para essa decisão, então ela não precisa ficar visível no resto da função.


Agora é com você

Tente resolver no rascunho antes de abrir a resposta. Os códigos não são executados nesta página.

01Dá para fazer o bolo?Fácil

A receita pede 3 ovos e 250 g de farinha, e na cozinha há 1 ovo, 400 g de farinha e um forno funcionando. Escreva um programa que avise o que está faltando e diga, no fim, se dá para fazer o bolo.

↳ Requisitos
  • Cada ingrediente é conferido com um if sem else, que só imprime quando falta algo.
  • A quantidade que falta é calculada no programa, e não escrita à mão.
  • A decisão final usa um único if/else com as três condições ligadas por &&.
  • A saída é idêntica à esperada.
código inicial
package main

import "fmt"

func main() {
	const ovosReceita = 3
	const farinhaReceita = 250.0

	ovos := 1
	farinha := 400.0
	fornoFunciona := true

	// confira cada ingrediente e decida se dá para fazer o bolo
}
saída esperada
Faltam 2 ovos.
Ainda não dá para fazer o bolo.
Ver dica

Os avisos e a decisão final são independentes: um ingrediente em falta não impede que os outros sejam conferidos.

Ver resposta
package main

import "fmt"

func main() {
	// O que a receita pede.
	const ovosReceita = 3
	const farinhaReceita = 250.0

	// O que há na cozinha.
	ovos := 1
	farinha := 400.0
	fornoFunciona := true

	// Cada ingrediente é conferido com um if sozinho.
	if ovos < ovosReceita {
		fmt.Printf("Faltam %d ovos.\n", ovosReceita-ovos)
	}
	if farinha < farinhaReceita {
		fmt.Printf("Faltam %.0f g de farinha.\n", farinhaReceita-farinha)
	}

	// A decisão final junta as três condições com &&.
	if ovos >= ovosReceita && farinha >= farinhaReceita && fornoFunciona {
		fmt.Println("Pode fazer o bolo!")
	} else {
		fmt.Println("Ainda não dá para fazer o bolo.")
	}
}

Passo a passo

  1. As constantes ovosReceita e farinhaReceita guardam o que a receita pede; farinhaReceita tem ponto decimal para combinar com farinha, que é float64.
  2. ovos, farinha e fornoFunciona descrevem a cozinha: um int, um float64 e um bool.
  3. if ovos < ovosReceita só imprime quando faltam ovos; a diferença ovosReceita-ovos é calculada na hora e dá 2.
  4. O segundo if faz o mesmo com a farinha. Como há 400 g, a condição é falsa e nada é impresso.
  5. O último if junta as três condições com &&: basta uma ser falsa para o else rodar.
  6. fornoFunciona já é bool, então entra na condição sem comparação extra (não precisa de == true).

Por que funciona

Os dois primeiros if são independentes entre si: cada um roda ou não, sem afetar o outro, e por isso todos os ingredientes em falta seriam listados. Já a decisão final é um if/else, em que exatamente um dos dois blocos roda.

O && só dá true quando todas as partes são verdadeiras. Como ovos >= ovosReceita é falso, o Go nem chega a avaliar as outras duas (curto-circuito) e vai direto para o else.

Erros comuns

  • Usar else if entre os avisos (if ovos < ... {} else if farinha < ... {}): quando faltam ovos, a farinha deixa de ser conferida. Avisos independentes pedem if separados.
  • Escrever if fornoFunciona == "sim": não compila (mismatched types bool and untyped string). Um bool só recebe true ou false.
  • Colocar o else na linha de baixo do }: o Go insere um ponto e vírgula automático no fim da linha e acusa syntax error: unexpected keyword else. Escreva } else {.
  • Usar || na decisão final: com 400 g de farinha a condição vira true e o programa diz que dá para fazer o bolo sem os ovos.

Outra forma de resolver

Dá para guardar cada teste numa variável bool com nome e usar os nomes no if:

temOvos := ovos >= ovosReceita
temFarinha := farinha >= farinhaReceita
if temOvos && temFarinha && fornoFunciona {
	fmt.Println("Pode fazer o bolo!")
}

Essa forma é preferível quando a mesma condição aparece em mais de um lugar ou quando a regra fica longa demais para ler numa linha.

Saída esperada

Faltam 2 ovos.
Ainda não dá para fazer o bolo.

02Situação no boletim com else ifMédio

Escreva a função situacao(nota float64, frequencia int) string com as regras da escola: frequência abaixo de 75% reprova por falta; com frequência suficiente, nota 7 ou mais aprova, de 5 a menos de 7 vai para recuperação e abaixo de 5 reprova por nota. Mostre o resultado dos cinco alunos da saída esperada.

↳ Requisitos
  • A função usa uma única cadeia if / else if / else.
  • A frequência é a primeira condição testada, para reprovar por falta mesmo com nota alta.
  • Os limites são inclusivos: nota 7.0 aprova e frequência 75 é suficiente.
  • Cada linha do main usa fmt.Printf com %% para imprimir o símbolo de porcentagem.
código inicial
package main

import "fmt"

func situacao(nota float64, frequencia int) string {
	// escreva as regras com if / else if / else
	return ""
}

func main() {
	fmt.Println("Resultado do 7º A:")
	fmt.Printf("%-7s nota %4.1f, freq. %3d%% -> %s\n", "Alice", 8.5, 92, situacao(8.5, 92))
	// mostre os outros alunos
}
saída esperada
Resultado do 7º A:
Alice   nota  8.5, freq.  92% -> aprovado
Bento   nota  6.0, freq.  80% -> recuperação
Clara   nota  9.5, freq.  70% -> reprovado por falta
Davi    nota  4.0, freq. 100% -> reprovado por nota
Elisa   nota  7.0, freq.  75% -> aprovado
Ver dica

Pense em qual regra vence as outras: se a frequência não for testada antes da nota, a Clara seria aprovada.

Ver resposta
package main

import "fmt"

// situacao decide o resultado do aluno pela frequência e pela nota.
func situacao(nota float64, frequencia int) string {
	// A frequência vem primeiro: abaixo de 75% reprova, seja qual for a nota.
	if frequencia < 75 {
		return "reprovado por falta"
	} else if nota >= 7 {
		return "aprovado"
	} else if nota >= 5 {
		return "recuperação"
	} else {
		return "reprovado por nota"
	}
}

func main() {
	fmt.Println("Resultado do 7º A:")
	fmt.Printf("%-7s nota %4.1f, freq. %3d%% -> %s\n", "Alice", 8.5, 92, situacao(8.5, 92))
	fmt.Printf("%-7s nota %4.1f, freq. %3d%% -> %s\n", "Bento", 6.0, 80, situacao(6.0, 80))
	fmt.Printf("%-7s nota %4.1f, freq. %3d%% -> %s\n", "Clara", 9.5, 70, situacao(9.5, 70))
	fmt.Printf("%-7s nota %4.1f, freq. %3d%% -> %s\n", "Davi", 4.0, 100, situacao(4.0, 100))
	fmt.Printf("%-7s nota %4.1f, freq. %3d%% -> %s\n", "Elisa", 7.0, 75, situacao(7.0, 75))
}

Passo a passo

  1. if frequencia < 75 vem primeiro porque a falta reprova independentemente da nota; é o caso da Clara, com 9.5 e 70%.
  2. else if nota >= 7 só é avaliado quando a frequência já é suficiente, então não precisa repetir frequencia >= 75 com &&.
  3. else if nota >= 5 pega as notas de 5 até menos de 7: a faixa de cima já foi descartada pelo teste anterior.
  4. O else final cobre o que sobrou, notas abaixo de 5, e garante que a função sempre devolve um valor.
  5. No main, %-7s alinha os nomes, %4.1f mostra a nota com uma casa e %% imprime o % depois da frequência.
  6. A Elisa, com 7.0 e 75%, testa os limites: como as comparações usam >= e <, ela é aprovada.

Por que funciona

Numa cadeia de else if, cada condição só é testada se as anteriores foram falsas, então cada faixa fica simples: quando o programa chega em nota >= 5, já sabe que a frequência é suficiente e que a nota é menor que 7. A ordem das condições é, na prática, parte da regra de negócio.

Como o else final não tem condição, algum return sempre acontece, e o compilador aceita a função sem um return extra no fim.

Erros comuns

  • Testar a nota antes da frequência: a Clara (9.5 e 70%) sai como aprovado, e a regra da falta nunca é aplicada.
  • Usar nota > 7 em vez de nota >= 7: a Elisa, com exatamente 7.0, cai em recuperação. Confira sempre o que acontece no valor do limite.
  • Trocar o else final por else if nota < 5 sem um return depois da cadeia: o compilador acusa missing return, porque não consegue provar que algum caminho sempre devolve um valor.
  • Escrever freq. %3d% -> com um % só: o Printf lê % - como outro verbo e a saída vira 92%!>(string=aprovado) %!s(MISSING). Dobre para %%.

Outra forma de resolver

Como cada ramo termina em return, dá para dispensar os else (estilo de retorno antecipado, comum em Go):

if frequencia < 75 {
	return "reprovado por falta"
}
if nota >= 7 {
	return "aprovado"
}
if nota >= 5 {
	return "recuperação"
}
return "reprovado por nota"

É preferível quando os blocos crescem, porque o código não vai sendo empurrado para a direita.

Saída esperada

Resultado do 7º A:
Alice   nota  8.5, freq.  92% -> aprovado
Bento   nota  6.0, freq.  80% -> recuperação
Clara   nota  9.5, freq.  70% -> reprovado por falta
Davi    nota  4.0, freq. 100% -> reprovado por nota
Elisa   nota  7.0, freq.  75% -> aprovado

03Teste de agilidade no d20Desafio

Num jogo de RPG, o jogador digita o valor tirado num dado de 20 lados, e a dificuldade do teste é 12. Escreva a função resultado(entrada string) string que converte o texto com strconv.Atoi e classifica a jogada: inválida, fora do dado, acerto crítico (20), falha crítica (1), sucesso ou falha com quanto faltou.

↳ Requisitos
  • A conversão usa if com instrução curta (if n, err := strconv.Atoi(entrada); err != nil) e n não existe fora dessa cadeia.
  • Valores fora de 1 a 20 são recusados com uma única condição usando ||.
  • Os críticos são testados antes da comparação com a dificuldade, e a dificuldade é uma constante.
  • Na falha, a diferença para a dificuldade é calculada e incluída no texto com fmt.Sprintf.
código inicial
package main

import (
	"fmt"
	"strconv"
)

const dificuldade = 12

func resultado(entrada string) string {
	// converta com strconv.Atoi e classifique a jogada
	_ = strconv.Atoi
	return ""
}

func main() {
	fmt.Printf("Teste de agilidade (dificuldade %d)\n", dificuldade)
	fmt.Printf("%-4s -> %s\n", "17", resultado("17"))
	// teste as outras jogadas: 8, 20, 1, 12, 25 e dez
}
saída esperada
Teste de agilidade (dificuldade 12)
17   -> sucesso
8    -> falha (faltaram 4)
20   -> acerto crítico!
1    -> falha crítica!
12   -> sucesso
25   -> fora do dado
dez  -> entrada inválida
Ver dica

Comece pelos casos que impedem qualquer outra análise (texto que não é número, valor impossível) e deixe a comparação com a dificuldade para o fim.

Ver resposta
package main

import (
	"fmt"
	"strconv"
)

const dificuldade = 12

// resultado interpreta uma jogada de d20 digitada pelo jogador.
func resultado(entrada string) string {
	// Instrução curta: n e err só existem dentro deste if/else.
	if n, err := strconv.Atoi(entrada); err != nil {
		return "entrada inválida"
	} else if n < 1 || n > 20 {
		return "fora do dado"
	} else if n == 20 {
		return "acerto crítico!"
	} else if n == 1 {
		return "falha crítica!"
	} else if n >= dificuldade {
		return "sucesso"
	} else {
		return fmt.Sprintf("falha (faltaram %d)", dificuldade-n)
	}
}

func main() {
	fmt.Printf("Teste de agilidade (dificuldade %d)\n", dificuldade)
	fmt.Printf("%-4s -> %s\n", "17", resultado("17"))
	fmt.Printf("%-4s -> %s\n", "8", resultado("8"))
	fmt.Printf("%-4s -> %s\n", "20", resultado("20"))
	fmt.Printf("%-4s -> %s\n", "1", resultado("1"))
	fmt.Printf("%-4s -> %s\n", "12", resultado("12"))
	fmt.Printf("%-4s -> %s\n", "25", resultado("25"))
	fmt.Printf("%-4s -> %s\n", "dez", resultado("dez"))
}

Passo a passo

  1. const dificuldade = 12 fica no nível do pacote, visível para resultado e para main; mudar o teste exige trocar um único valor.
  2. if n, err := strconv.Atoi(entrada); err != nil converte o texto e, na mesma linha, testa o erro. Para "dez", a conversão falha e a função devolve entrada inválida.
  3. Os else if seguintes enxergam n, porque as variáveis da instrução curta valem para toda a cadeia; fora dela, n não existe.
  4. n < 1 || n > 20 recusa qualquer valor impossível num d20 com uma só condição: basta um dos lados ser verdadeiro.
  5. n == 20 e n == 1 vêm antes de n >= dificuldade, senão o 20 cairia em sucesso e o crítico nunca apareceria.
  6. n >= dificuldade usa >= porque tirar exatamente 12 passa no teste, como mostra a penúltima jogada válida.
  7. No else, fmt.Sprintf monta o texto com a diferença calculada, sem imprimir nada; quem imprime é o main.
  8. O main chama a função com sete entradas que cobrem todos os ramos, inclusive os limites 1, 12 e 20.

Por que funciona

A função é uma única cadeia de decisões, ordenada do caso mais geral de rejeição até o mais comum. Cada condição só é testada se as anteriores falharam: quando o programa chega em n == 20, já sabe que o texto era um número e que está entre 1 e 20. Isso permite escrever cada teste de forma curta, sem repetir verificações.

A instrução curta amarra a conversão à decisão: o err é testado logo depois de criado e n só fica disponível onde a conversão deu certo, que é exatamente onde ele pode ser usado. Esse padrão (chamar, testar err != nil, seguir) aparece em praticamente todo código Go que lida com dados de fora do programa.

Erros comuns

  • Testar n >= dificuldade antes de n == 20: o 20 é classificado como sucesso e a linha do crítico nunca aparece. Casos especiais vêm antes das faixas que os contêm.
  • Escrever n < 1 && n > 20: nenhum número é menor que 1 e maior que 20 ao mesmo tempo, então a condição é sempre falsa e o 25 vira sucesso. Use ||.
  • Declarar n, err := strconv.Atoi(entrada) numa linha e usar if err != nil depois funciona, mas deixa n visível no resto da função mesmo quando a conversão falhou (com valor 0). A instrução curta evita esse uso indevido.
  • Ignorar o erro com n, _ := strconv.Atoi(entrada): o texto "dez" vira 0 e a jogada sai como fora do dado, escondendo que a entrada nem era um número.
  • Usar fmt.Printf dentro de resultado: a função passa a imprimir em vez de devolver o texto, e o main mostra a mensagem fora do lugar, seguida de uma string vazia.

Outra forma de resolver

Com retornos antecipados, a conversão sai da cadeia e cada regra vira um if separado:

n, err := strconv.Atoi(entrada)
if err != nil {
	return "entrada inválida"
}
if n < 1 || n > 20 {
	return "fora do dado"
}
// ... críticos, sucesso e falha

Esse é o estilo mais comum quando a função é longa, porque o caso de erro é resolvido e esquecido logo no início. A cadeia com instrução curta é preferível quando tudo cabe numa única decisão, como aqui, e você quer que n não escape dela. Quando os casos são valores fixos, como 1 e 20, o switch (próximas lições) também é uma boa opção.

Saída esperada

Teste de agilidade (dificuldade 12)
17   -> sucesso
8    -> falha (faltaram 4)
20   -> acerto crítico!
1    -> falha crítica!
12   -> sucesso
25   -> fora do dado
dez  -> entrada inválida

Últimas lições

Ver o arquivo completo →