Golang: For e Condicionais (Parte 15)
Golang: For e Condicionais (Parte 15)
Em Go, as declarações de controle de fluxo são projetadas para serem simples, claras e eficientes. Diferente de muitas outras linguagens, Go possui apenas uma construção de loop: for. As condicionais são tratadas pelas declarações if e switch.
1. O Loop for
O loop for do Go é incrivelmente versátil, servindo ao propósito de loops for, while e do-while encontrados em outras linguagens.
1.1. for Básico (como while)
A forma mais básica de for atua como um loop while. Ele continua a ser executado enquanto uma condição for verdadeira.
package main
import "fmt"
func main() {
// Loop 'for' básico (como um loop 'while')
i := 0
for i < 5 {
fmt.Printf("Loop básico: %d\n", i)
i++
}
}
1.2. for Tradicional (init; condition; post)
Esta é a forma mais comum, semelhante aos loops for estilo C. Possui três componentes separados por ponto e vírgula:
initstatement: Executado antes da primeira iteração. Variáveis declaradas aqui têm escopo limitado ao loopfor.conditionexpression: Avaliada antes de cada iteração. Setrue, o loop continua; caso contrário, ele termina.poststatement: Executado após cada iteração.
package main
import "fmt"
func main() {
// Loop 'for' tradicional
for j := 0; j < 5; j++ {
fmt.Printf("Loop tradicional: %d\n", j)
}
}
1.3. for Infinito
Se você omitir a condição, você cria um loop infinito. Isso é frequentemente usado em aplicações de servidor ou processos em segundo plano que rodam indefinidamente, tipicamente com uma declaração break explícita dentro.
package main
import "fmt"
func main() {
// Loop 'for' infinito
k := 0
for {
fmt.Printf("Loop infinito: %d\n", k)
k++
if k >= 3 {
break // Sai do loop após 3 iterações
}
}
}
1.4. for...range Loop
A construção for...range é usada para iterar sobre elementos de várias estruturas de dados:
- Arrays e Slices: Retorna o índice e uma cópia do elemento.
- Strings: Retorna o índice de byte inicial do rune e o próprio rune.
- Maps: Retorna a chave e o valor.
- Channels: Retorna o valor recebido do channel (até que seja fechado).
Você pode omitir o índice ou o valor se não precisar deles usando o identificador em branco _.
package main
import "fmt"
func main() {
// for...range com um slice
numbers := []int{10, 20, 30, 40, 50}
for index, value := range numbers {
fmt.Printf("Elemento do slice no índice %d: %d\n", index, value)
}
// for...range com uma string (itera sobre runes)
greeting := "Olá, Mundo"
for index, runeValue := range greeting {
fmt.Printf("Rune da string no índice de byte %d: %c (Unicode: %U)\n", index, runeValue, runeValue)
}
// for...range com um map
ages := map[string]int{"Alice": 30, "Bob": 25, "Charlie": 35}
for name, age := range ages {
fmt.Printf("%s tem %d anos.\n", name, age)
}
// Omitindo índice/chave ou valor
for _, value := range numbers { // Apenas o valor
fmt.Printf("Valor (sem índice): %d\n", value)
}
for index, _ := range numbers { // Apenas o índice
fmt.Printf("Índice (sem valor): %d\n", index)
}
}
1.5. break e continue
break: Termina o loopformais interno imediatamente.continue: Pula o restante da iteração atual e prossegue para a próxima iteração do loop.
package main
import "fmt"
func main() {
// Usando continue
fmt.Println("Usando continue:")
for i := 0; i < 5; i++ {
if i%2 == 0 { // Pula números pares
continue
}
fmt.Printf("Número ímpar: %d\n", i)
}
// Usando break
fmt.Println("\nUsando break:")
for i := 0; i < 10; i++ {
if i == 5 {
break // Sai do loop quando i for 5
}
fmt.Printf("Número antes do break: %d\n", i)
}
}
1.6. break e continue Rotulados
Go permite que você use rótulos com break e continue para controlar qual loop eles afetam, especialmente útil em loops aninhados.
package main
import "fmt"
func main() {
OuterLoop: // Rótulo para o loop externo
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
fmt.Printf("i: %d, j: %d\n", i, j)
if i == 1 && j == 1 {
break OuterLoop // Sai do OuterLoop
}
}
}
fmt.Println("Saiu do OuterLoop")
InnerLoop: // Rótulo para o loop interno
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
continue InnerLoop // Continua o InnerLoop (próxima iteração de j)
}
fmt.Printf("i: %d, j: %d\n", i, j)
}
}
fmt.Println("Exemplo de continue rotulado finalizado")
}
2. Declarações Condicionais
Go fornece if e switch para execução condicional.
2.1. Declaração if
A declaração if avalia uma expressão booleana e executa um bloco de código se a expressão for true. Diferente de C, C++ ou Java, os parênteses () em torno da condição não são obrigatórios, mas as chaves {} em torno do corpo são.
package main
import "fmt"
func main() {
x := 10
if x > 5 {
fmt.Println("x é maior que 5")
}
}
2.2. Declaração if...else
O bloco else é executado se a condição if for false.
package main
import "fmt"
func main() {
x := 3
if x > 5 {
fmt.Println("x é maior que 5")
} else {
fmt.Println("x não é maior que 5")
}
}
2.3. Cadeia if...else if...else
Para múltiplas condições, você pode encadear cláusulas else if.
package main
import "fmt"
func main() {
score := 85
if score >= 90 {
fmt.Println("Nota: A")
} else if score >= 80 {
fmt.Println("Nota: B")
} else if score >= 70 {
fmt.Println("Nota: C")
} else {
fmt.Println("Nota: F")
}
}
2.4. if com Declaração Curta
Um idioma comum em Go é incluir uma declaração curta antes da condição. Esta declaração é tipicamente usada para inicializar uma variável que é então usada na condição. O escopo da variável é limitado aos blocos if e else.
package main
import (
"fmt"
"strconv" // Pacote para conversão de string
)
func main() {
// Exemplo 1: Verificação de erro
if num, err := strconv.Atoi("123"); err == nil {
fmt.Printf("Número convertido: %d\n", num)
} else {
fmt.Printf("Erro ao converter string: %v\n", err)
}
// Exemplo 2: Inicialização de variável
if length := len("Programação Go"); length > 10 {
fmt.Printf("String é longa, comprimento: %d\n", length)
} else {
fmt.Printf("String é curta, comprimento: %d\n", length)
}
// 'num' e 'length' não são acessíveis aqui
// fmt.Println(num) // Isso causaria um erro de compilação
}
Este padrão é particularmente útil para tratamento de erros, pois permite que você verifique um erro imediatamente após uma chamada de função que pode retornar um.
2.5. Declaração switch
A declaração switch fornece uma maneira mais limpa de escrever longas cadeias if-else if.
2.5.1. switch Básico
O switch do Go é mais flexível do que em muitas outras linguagens. Ele automaticamente fornece um break após cada case, o que significa que a execução não "cai" para o próximo case por padrão.
package main
import "fmt"
func main() {
day := "Wednesday"
switch day {
case "Monday":
fmt.Println("Início da semana.")
case "Tuesday", "Wednesday", "Thursday": // Múltiplas expressões em um case
fmt.Println("Meio da semana.")
case "Friday":
fmt.Println("Quase fim de semana!")
default: // Caso default opcional se nenhum outro case corresponder
fmt.Println("Fim de semana!")
}
}
2.5.2. switch sem uma Expressão (Tagless Switch)
Uma declaração switch pode ser usada sem uma expressão, efetivamente tornando-se uma maneira mais limpa de escrever a lógica if-else if-else. Cada case então contém uma expressão booleana.
package main
import "fmt"
func main() {
age := 25
switch { // Nenhuma expressão aqui
case age < 18:
fmt.Println("Menor")
case age >= 18 && age < 65:
fmt.Println("Adulto")
default:
fmt.Println("Idoso")
}
}
2.5.3. Palavra-chave fallthrough
Se você deseja explicitamente que a execução "caia" para o próximo case (como em C/C++), você pode usar a palavra-chave fallthrough. Isso raramente é necessário e deve ser usado com cautela, pois pode tornar o código mais difícil de ler.
package main
import "fmt"
func main() {
num := 2
switch num {
case 1:
fmt.Println("Case 1")
fallthrough // A execução continua para o case 2
case 2:
fmt.Println("Case 2")
fallthrough // A execução continua para o case 3
case 3:
fmt.Println("Case 3")
default:
fmt.Println("Case default")
}
// Saída:
// Case 2
// Case 3
}
2.5.4. type switch
Um type switch é usado para determinar o tipo dinâmico de um valor de interface.
package main
import "fmt"
func printType(i interface{}) {
switch v := i.(type) { // 'v' é o valor com seu tipo concreto
case int:
fmt.Printf("Tipo: int, Valor: %d\n", v)
case string:
fmt.Printf("Tipo: string, Valor: %s\n", v)
case bool:
fmt.Printf("Tipo: bool, Valor: %t\n", v)
default:
fmt.Printf("Tipo desconhecido: %T, Valor: %v\n", v, v)
}
}
func main() {
printType(10)
printType("olá")
printType(true)
printType(3.14)
}
Melhores Práticas e Armadilhas Comuns
- Legibilidade: Prefira
for...rangepara iterar sobre coleções sempre que possível, pois é frequentemente mais conciso e menos propenso a erros do que o gerenciamento manual de índices. - Declarações Curtas: Aproveite a sintaxe de declaração curta de
ifeswitchpara declarar variáveis cujo escopo deve ser limitado ao bloco condicional, especialmente para tratamento de erros. switchvs.if-else if: Para mais de duas ou três condições distintas em uma única variável,switchgeralmente leva a um código mais limpo e legível do que uma longa cadeiaif-else if. Para lógica booleana complexa envolvendo múltiplas variáveis,if-else if(ou umswitchsem expressão) pode ser mais apropriado.fallthrough: Usefallthroughcom moderação. Ele pode tornar o código mais difícil de entender. A maioria dos programas Go não o utiliza.- Loops Infinitos: Ao criar loops infinitos (
for {}), sempre garanta que haja uma condiçãobreakclara ou que o loop seja gerenciado por sinais externos (por exemplo, fechamento de canais, cancelamento de contexto) em programas concorrentes. - Escopo de Variáveis: Esteja atento ao escopo das variáveis, especialmente com variáveis declaradas em declarações
initde loopforou declarações curtas deif/switch. Elas são acessíveis apenas dentro de seus respectivos blocos.
Este mergulho profundo cobre os aspectos essenciais dos loops for e das declarações condicionais em Go, fornecendo uma base sólida para escrever lógica de controle de fluxo em suas aplicações.