Bucles `for`: clásico, `while`-style y `range`
Go solo tiene una palabra clave para bucles: `for`. Con ella cubres el bucle clásico C-style, el `while` de otros lenguajes, los bucles infinitos y la iteración con `range`.
for clásico (estilo C)
El for clásico tiene tres componentes separados por ;: inicialización, condición y post. Igual que en C/Java, pero sin paréntesis alrededor.
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Print(i, " ")
}
fmt.Println()
// Suma de 1 a 100
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
fmt.Println("sum:", sum)
// Recorrer hacia atrás
for i := 5; i > 0; i-- {
fmt.Print(i, " ")
}
fmt.Println()
}
0 1 2 3 4
sum: 5050
5 4 3 2 1 for como while
Si omites la inicialización y el post, te queda solo la condición — equivalente al while de otros lenguajes. Go no tiene while ni do-while: todo es for.
package main
import "fmt"
func main() {
n := 1
for n < 100 {
n *= 2
}
fmt.Println("primer pow2 >= 100:", n)
// Leer hasta una condición
balance := 1000
day := 0
for balance > 0 {
balance -= 75
day++
}
fmt.Printf("balance agotado en %d días
", day)
}
primer pow2 >= 100: 128
balance agotado en 14 díasfor infinito + break y continue
Sin condición alguna, for {} es un bucle infinito. Se sale con break, return o un panic. continue salta a la siguiente iteración.
package main
import "fmt"
func main() {
i := 0
for {
i++
if i%2 == 0 {
continue
}
if i > 9 {
break
}
fmt.Print(i, " ")
}
fmt.Println()
// Equivale al patrón "loop con guarda interna"
count := 0
for {
if count >= 3 {
break
}
fmt.Println("tick", count)
count++
}
}
1 3 5 7 9
tick 0
tick 1
tick 2break y continue con etiquetas
Cuando tienes bucles anidados y necesitas salir del bucle exterior (o saltar a su siguiente iteración) usa labels: un identificador seguido de : antes del for.
package main
import "fmt"
func main() {
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
target := 5
outer:
for i, row := range matrix {
for j, v := range row {
if v == target {
fmt.Printf("encontrado %d en [%d][%d]
", v, i, j)
break outer // sale de ambos bucles
}
}
}
// continue con label: salta a la siguiente fila
rows:
for _, row := range matrix {
for _, v := range row {
if v%2 == 0 {
continue rows // descarta la fila completa al primer par
}
fmt.Print(v, " ")
}
}
fmt.Println()
}
encontrado 5 en [1][1]
1 7 for range: slices, maps, strings y channels
range produce uno o dos valores según lo que itere. Convención: descarta los que no uses con _.
package main
import "fmt"
func main() {
// Slice: índice y valor
fruits := []string{"manzana", "pera", "uva"}
for i, v := range fruits {
fmt.Printf("%d -> %s
", i, v)
}
// Solo el índice
for i := range fruits {
fmt.Print(i, " ")
}
fmt.Println()
// Solo el valor: descarta el índice con _
total := 0
for _, n := range []int{10, 20, 30} {
total += n
}
fmt.Println("total:", total)
// Map: clave y valor (orden NO garantizado)
scores := map[string]int{"ana": 90, "luis": 75}
for k, v := range scores {
fmt.Printf("%s=%d ", k, v)
}
fmt.Println()
}
0 -> manzana
1 -> pera
2 -> uva
0 1 2
total: 60
ana=90 luis=75 range sobre strings y channels
Iterar un string con range produce runes (puntos de código Unicode), no bytes. El índice es el offset en bytes, así que puede saltar valores en caracteres multibyte. Iterar un channel consume valores hasta que se cierra.
package main
import "fmt"
func main() {
// range sobre string: (byteIndex, rune)
for i, r := range "héllo" {
fmt.Printf("%d: %c (%d)
", i, r, r)
}
// range sobre channel: lee hasta close()
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for v := range ch {
fmt.Print(v, " ")
}
fmt.Println()
}
0: h (104)
1: é (233)
3: l (108)
4: l (108)
5: o (111)
1 2 3 é ocupa 2 bytes en UTF-8, por eso el índice pasa de 1 a 3. Si necesitas contar caracteres reales, itera con range (cuenta runes) o usa utf8.RuneCountInString.