Control de Flujo
Control de Flujo
if / else
En Go, las condiciones if no llevan parentesis pero las llaves son obligatorias:
package main
import "fmt"
func main() {
edad := 25
if edad >= 18 {
fmt.Println("Mayor de edad")
} else if edad >= 13 {
fmt.Println("Adolescente")
} else {
fmt.Println("Menor de edad")
}
}
if con init statement
Go permite declarar una variable dentro del if. Su scope se limita al bloque if/else:
package main
import (
"fmt"
"os"
)
func main() {
// La variable err solo existe dentro del if/else
if err := os.Setenv("APP_MODE", "dev"); err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Variable establecida")
}
// err no existe aqui
}
Este patron es idiomatico en Go y se usa constantemente para manejo de errores:
package main
import (
"fmt"
"strconv"
)
func main() {
if valor, err := strconv.Atoi("42"); err != nil {
fmt.Println("Error de conversion:", err)
} else {
fmt.Println("Valor:", valor)
}
}
for: el unico loop en Go
Go tiene un solo tipo de loop: for. No existe while ni do-while.
for clasico (estilo C)
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Print(i, " ")
}
// 0 1 2 3 4
}
for como while
package main
import "fmt"
func main() {
contador := 0
for contador < 5 {
fmt.Print(contador, " ")
contador++
}
// 0 1 2 3 4
}
for infinito
package main
import "fmt"
func main() {
i := 0
for {
if i >= 3 {
break
}
fmt.Print(i, " ")
i++
}
// 0 1 2
}
for range
range itera sobre colecciones (slices, maps, strings, channels):
package main
import "fmt"
func main() {
// Range sobre slice
frutas := []string{"manzana", "pera", "uva"}
for indice, fruta := range frutas {
fmt.Printf("[%d] %s\n", indice, fruta)
}
// Ignorar indice con _
for _, fruta := range frutas {
fmt.Println(fruta)
}
// Solo indices
for i := range frutas {
fmt.Println(i)
}
// Range sobre string (itera runes, no bytes)
for i, r := range "Hola" {
fmt.Printf("byte %d: %c (U+%04X)\n", i, r, r)
}
}
Tabla: variantes del for
| Forma | Equivalente en otros lenguajes | Ejemplo |
|---|---|---|
for i := 0; i < n; i++ | for clasico | Iterar con contador |
for condicion | while | Repetir mientras se cumpla |
for | while true | Loop infinito |
for i, v := range col | for...of / foreach | Iterar colecciones |
switch
El switch de Go no necesita break: cada caso termina automaticamente. Para continuar al siguiente caso se usa fallthrough.
switch basico
package main
import "fmt"
func main() {
dia := "martes"
switch dia {
case "lunes":
fmt.Println("Inicio de semana")
case "martes", "miercoles", "jueves":
fmt.Println("Entre semana")
case "viernes":
fmt.Println("Casi fin de semana")
case "sabado", "domingo":
fmt.Println("Fin de semana")
default:
fmt.Println("Dia invalido")
}
// Entre semana
}
switch sin expresion (reemplaza if/else if)
package main
import "fmt"
func clasificarNota(nota int) string {
switch {
case nota >= 90:
return "Excelente"
case nota >= 80:
return "Bueno"
case nota >= 70:
return "Regular"
default:
return "Insuficiente"
}
}
func main() {
fmt.Println(clasificarNota(85)) // Bueno
fmt.Println(clasificarNota(95)) // Excelente
}
switch con init statement
package main
import (
"fmt"
"runtime"
)
func main() {
switch os := runtime.GOOS; os {
case "linux":
fmt.Println("Estas en Linux")
case "darwin":
fmt.Println("Estas en macOS")
case "windows":
fmt.Println("Estas en Windows")
default:
fmt.Println("SO:", os)
}
}
fallthrough
package main
import "fmt"
func main() {
nivel := 1
switch nivel {
case 1:
fmt.Println("Acceso basico")
fallthrough
case 2:
fmt.Println("Acceso intermedio")
fallthrough
case 3:
fmt.Println("Acceso completo")
}
// Acceso basico
// Acceso intermedio
// Acceso completo
}
type switch
Permite verificar el tipo concreto de una interface:
package main
import "fmt"
func describir(i interface{}) string {
switch v := i.(type) {
case int:
return fmt.Sprintf("entero: %d", v)
case string:
return fmt.Sprintf("string: %q", v)
case bool:
return fmt.Sprintf("booleano: %t", v)
default:
return fmt.Sprintf("tipo desconocido: %T", v)
}
}
func main() {
fmt.Println(describir(42)) // entero: 42
fmt.Println(describir("hola")) // string: "hola"
fmt.Println(describir(true)) // booleano: true
fmt.Println(describir(3.14)) // tipo desconocido: float64
}
defer
defer pospone la ejecucion de una funcion hasta que la funcion contenedora retorna. Se ejecutan en orden LIFO (ultimo en entrar, primero en salir):
package main
import "fmt"
func main() {
fmt.Println("inicio")
defer fmt.Println("primero en defer")
defer fmt.Println("segundo en defer")
defer fmt.Println("tercero en defer")
fmt.Println("fin")
}
// inicio
// fin
// tercero en defer
// segundo en defer
// primero en defer
Uso practico: cerrar recursos
package main
import (
"fmt"
"os"
)
func leerArchivo(ruta string) error {
archivo, err := os.Open(ruta)
if err != nil {
return err
}
defer archivo.Close() // Se cierra al salir de la funcion
buf := make([]byte, 100)
n, err := archivo.Read(buf)
if err != nil {
return err
}
fmt.Printf("Leidos %d bytes: %s\n", n, buf[:n])
return nil
}
func main() {
if err := leerArchivo("/etc/hostname"); err != nil {
fmt.Println("Error:", err)
}
}
defer evalua argumentos inmediatamente
package main
import "fmt"
func main() {
x := 10
defer fmt.Println("defer x:", x) // Captura x=10 aqui
x = 20
fmt.Println("x:", x)
}
// x: 20
// defer x: 10
Labels con break y continue
Los labels permiten controlar loops anidados:
package main
import "fmt"
func main() {
// break con label sale del loop externo
externo:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break externo
}
fmt.Printf("(%d,%d) ", i, j)
}
}
fmt.Println()
// (0,0) (0,1) (0,2) (1,0)
// continue con label salta a la siguiente iteracion del loop externo
fmt.Println("---")
filas:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 {
continue filas
}
fmt.Printf("(%d,%d) ", i, j)
}
}
// (0,0) (1,0) (2,0)
}
goto
Go soporta goto pero su uso es muy desaconsejado. Solo es util en codigo generado o para salir de multiples niveles de anidacion sin labels:
package main
import "fmt"
func main() {
i := 0
loop:
if i < 3 {
fmt.Println(i)
i++
goto loop
}
// 0
// 1
// 2
}
En la practica, prefiere for con break/continue y labels.
Ejercicios
- Escribe una funcion que determine si un numero es primo usando
for - Implementa FizzBuzz con
switchsin expresion - Crea una funcion que use
deferpara medir el tiempo de ejecucion
Resumen
ifsin parentesis, con init statement opcionalfores el unico loop: clasico, while, infinito y rangeswitchsinbreakimplicito, confallthroughexplicitodeferejecuta en LIFO al salir de la funcion- Labels permiten
break/continueen loops anidados gotoexiste pero su uso es desaconsejado