Decisões com condicionais
por Ellery BlackwoodEntenda 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.
if condição { ... } else { ... }: sem parênteses na condição, com chaves obrigatórias e oelsena mesma linha do}.- A condição precisa ser
bool;if estoque { ... }com umintnão compila, escrevaif 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 doife dos seuselse.
Do conceito à prática
3 exemplos para ver a ideia funcionando.
01Estoque da loja com if e else
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 ||
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
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.
- Cada ingrediente é conferido com um
ifsemelse, 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/elsecom as três condições ligadas por&&. - A saída é idêntica à esperada.
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
}
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
- As constantes
ovosReceitaefarinhaReceitaguardam o que a receita pede;farinhaReceitatem ponto decimal para combinar comfarinha, que éfloat64. ovos,farinhaefornoFuncionadescrevem a cozinha: umint, umfloat64e umbool.if ovos < ovosReceitasó imprime quando faltam ovos; a diferençaovosReceita-ovosé calculada na hora e dá2.- O segundo
iffaz o mesmo com a farinha. Como há 400 g, a condição é falsa e nada é impresso. - O último
ifjunta as três condições com&&: basta uma ser falsa para oelserodar. fornoFuncionajá é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 ifentre os avisos (if ovos < ... {} else if farinha < ... {}): quando faltam ovos, a farinha deixa de ser conferida. Avisos independentes pedemifseparados. - Escrever
if fornoFunciona == "sim": não compila (mismatched types bool and untyped string). Umboolsó recebetrueoufalse. - Colocar o
elsena linha de baixo do}: o Go insere um ponto e vírgula automático no fim da linha e acusasyntax error: unexpected keyword else. Escreva} else {. - Usar
||na decisão final: com 400 g de farinha a condição viratruee 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.
- 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.0aprova e frequência75é suficiente. - Cada linha do
mainusafmt.Printfcom%%para imprimir o símbolo de porcentagem.
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
}
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
if frequencia < 75vem primeiro porque a falta reprova independentemente da nota; é o caso da Clara, com 9.5 e 70%.else if nota >= 7só é avaliado quando a frequência já é suficiente, então não precisa repetirfrequencia >= 75com&&.else if nota >= 5pega as notas de 5 até menos de 7: a faixa de cima já foi descartada pelo teste anterior.- O
elsefinal cobre o que sobrou, notas abaixo de 5, e garante que a função sempre devolve um valor. - No
main,%-7salinha os nomes,%4.1fmostra a nota com uma casa e%%imprime o%depois da frequência. - 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 > 7em vez denota >= 7: a Elisa, com exatamente 7.0, cai emrecuperação. Confira sempre o que acontece no valor do limite. - Trocar o
elsefinal porelse if nota < 5sem umreturndepois da cadeia: o compilador acusamissing return, porque não consegue provar que algum caminho sempre devolve um valor. - Escrever
freq. %3d% ->com um%só: oPrintflê% -como outro verbo e a saída vira92%!>(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.
- A conversão usa
ifcom instrução curta (if n, err := strconv.Atoi(entrada); err != nil) ennã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.
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
}
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
const dificuldade = 12fica no nível do pacote, visível pararesultadoe paramain; mudar o teste exige trocar um único valor.if n, err := strconv.Atoi(entrada); err != nilconverte o texto e, na mesma linha, testa o erro. Para"dez", a conversão falha e a função devolveentrada inválida.- Os
else ifseguintes enxergamn, porque as variáveis da instrução curta valem para toda a cadeia; fora dela,nnão existe. n < 1 || n > 20recusa qualquer valor impossível num d20 com uma só condição: basta um dos lados ser verdadeiro.n == 20en == 1vêm antes den >= dificuldade, senão o 20 cairia emsucessoe o crítico nunca apareceria.n >= dificuldadeusa>=porque tirar exatamente 12 passa no teste, como mostra a penúltima jogada válida.- No
else,fmt.Sprintfmonta o texto com a diferença calculada, sem imprimir nada; quem imprime é omain. - O
mainchama 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 >= dificuldadeantes den == 20: o 20 é classificado comosucessoe 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 virasucesso. Use||. - Declarar
n, err := strconv.Atoi(entrada)numa linha e usarif err != nildepois funciona, mas deixanvisível no resto da função mesmo quando a conversão falhou (com valor0). A instrução curta evita esse uso indevido. - Ignorar o erro com
n, _ := strconv.Atoi(entrada): o texto"dez"vira0e a jogada sai comofora do dado, escondendo que a entrada nem era um número. - Usar
fmt.Printfdentro deresultado: a função passa a imprimir em vez de devolver o texto, e omainmostra 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