Funciones
Funciones
Declaracion basica
Las funciones en Go se declaran con func:
package main
import "fmt"
func saludar(nombre string) string {
return "Hola, " + nombre
}
func sumar(a, b int) int {
return a + b
}
func main() {
fmt.Println(saludar("Go")) // Hola, Go
fmt.Println(sumar(3, 7)) // 10
}
Cuando varios parametros consecutivos comparten tipo, se puede abreviar:
// Ambas son equivalentes
func sumar(a int, b int) int { return a + b }
func sumar(a, b int) int { return a + b }
Retornos multiples
Go permite retornar multiples valores. Este es uno de los patrones mas idiomaticos del lenguaje, especialmente para manejo de errores:
package main
import (
"errors"
"fmt"
)
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division por cero")
}
return a / b, nil
}
func main() {
resultado, err := dividir(10, 3)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("%.2f\n", resultado) // 3.33
_, err = dividir(10, 0)
if err != nil {
fmt.Println("Error:", err) // Error: division por cero
}
}
El patron valor, err := funcion() seguido de if err != nil es la forma estandar de manejar errores en Go.
Named returns
Los valores de retorno pueden tener nombre. Se inicializan con su zero value y se retornan con return sin argumentos:
package main
import "fmt"
func coordenadas(angulo float64) (x, y float64) {
// x e y ya estan declaradas con valor 0.0
x = angulo * 2
y = angulo * 3
return // retorna x e y implicitamente
}
func estadisticas(nums []int) (min, max, suma int) {
if len(nums) == 0 {
return // retorna 0, 0, 0
}
min = nums[0]
max = nums[0]
for _, n := range nums {
if n < min {
min = n
}
if n > max {
max = n
}
suma += n
}
return
}
func main() {
x, y := coordenadas(5.0)
fmt.Printf("x=%.1f, y=%.1f\n", x, y) // x=10.0, y=15.0
min, max, suma := estadisticas([]int{3, 1, 4, 1, 5, 9})
fmt.Printf("min=%d, max=%d, suma=%d\n", min, max, suma)
// min=1, max=9, suma=23
}
Usa named returns para funciones cortas. En funciones largas, el return explicito es mas legible.
Funciones variadic
Las funciones variadic aceptan un numero variable de argumentos con ...:
package main
import "fmt"
func sumarTodos(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func imprimir(prefijo string, valores ...interface{}) {
fmt.Print(prefijo + ": ")
for _, v := range valores {
fmt.Printf("%v ", v)
}
fmt.Println()
}
func main() {
fmt.Println(sumarTodos(1, 2, 3)) // 6
fmt.Println(sumarTodos(10, 20, 30, 40)) // 100
fmt.Println(sumarTodos()) // 0
// Expandir un slice con ...
numeros := []int{5, 10, 15}
fmt.Println(sumarTodos(numeros...)) // 30
imprimir("datos", "Go", 1.22, true)
// datos: Go 1.22 true
}
fmt.Println es un ejemplo de funcion variadic de la stdlib:
// Firma real de fmt.Println
func Println(a ...interface{}) (n int, err error)
Funciones como valores (first-class)
Las funciones en Go son valores de primera clase. Se pueden asignar a variables, pasar como argumentos y retornar desde otras funciones:
package main
import (
"fmt"
"strings"
)
func aplicar(s string, fn func(string) string) string {
return fn(s)
}
func main() {
// Asignar funcion a variable
gritar := strings.ToUpper
fmt.Println(gritar("hola")) // HOLA
// Pasar funcion como argumento
resultado := aplicar("Hola Mundo", strings.ToLower)
fmt.Println(resultado) // hola mundo
// Type para funciones
type transformador func(string) string
var fn transformador = strings.Title
fmt.Println(fn("hola mundo"))
}
Funciones anonimas
Las funciones anonimas se declaran y ejecutan sin nombre:
package main
import "fmt"
func main() {
// Declarar y asignar
doble := func(n int) int {
return n * 2
}
fmt.Println(doble(5)) // 10
// Declarar y ejecutar inmediatamente (IIFE)
resultado := func(a, b int) int {
return a + b
}(3, 4)
fmt.Println(resultado) // 7
}
Closures
Un closure es una funcion anonima que captura variables de su scope externo:
package main
import "fmt"
func contador() func() int {
n := 0
return func() int {
n++
return n
}
}
func multiplicadorPor(factor int) func(int) int {
return func(n int) int {
return n * factor
}
}
func main() {
// Cada llamada a contador() crea un closure independiente
c1 := contador()
c2 := contador()
fmt.Println(c1()) // 1
fmt.Println(c1()) // 2
fmt.Println(c1()) // 3
fmt.Println(c2()) // 1 (independiente de c1)
doble := multiplicadorPor(2)
triple := multiplicadorPor(3)
fmt.Println(doble(5)) // 10
fmt.Println(triple(5)) // 15
}
Closure practico: middleware
package main
import "fmt"
func conLog(nombre string, fn func(int, int) int) func(int, int) int {
return func(a, b int) int {
resultado := fn(a, b)
fmt.Printf("[%s] %d, %d -> %d\n", nombre, a, b, resultado)
return resultado
}
}
func main() {
sumar := func(a, b int) int { return a + b }
sumarConLog := conLog("SUMA", sumar)
sumarConLog(3, 4) // [SUMA] 3, 4 -> 7
sumarConLog(10, 20) // [SUMA] 10, 20 -> 30
}
La funcion init()
init() se ejecuta automaticamente al inicializar el paquete, antes de main(). Puede haber multiples init() por archivo:
package main
import "fmt"
var config string
func init() {
// Se ejecuta antes de main
config = "produccion"
fmt.Println("init: configuracion cargada")
}
func main() {
fmt.Println("main: config =", config)
}
// init: configuracion cargada
// main: config = produccion
Orden de ejecucion:
- Variables a nivel de paquete
- Funciones
init()(en orden de aparicion) main()(solo enpackage main)
Usa init() con moderacion. Prefiere inicializacion explicita cuando sea posible.
Blank identifier _
El blank identifier _ descarta valores que no necesitas:
package main
import "fmt"
func infoUsuario() (string, int, string) {
return "Artiko", 30, "Go Developer"
}
func main() {
// Solo nos interesa el nombre y el rol
nombre, _, rol := infoUsuario()
fmt.Println(nombre, "-", rol) // Artiko - Go Developer
// Ignorar indice en range
colores := []string{"rojo", "verde", "azul"}
for _, color := range colores {
fmt.Println(color)
}
// Ignorar error (no recomendado en produccion)
valor, _ := fmt.Println("test")
_ = valor
}
Tabla resumen de tipos de funciones
| Tipo | Sintaxis | Uso |
|---|---|---|
| Basica | func nombre(args) ret | Funcion estandar |
| Retorno multiple | func f() (int, error) | Manejo de errores |
| Named return | func f() (x, y int) | Documentar retornos |
| Variadic | func f(args ...int) | N argumentos del mismo tipo |
| Anonima | func(x int) int { ... } | Callbacks, closures |
| Closure | Anonima que captura scope | Estado encapsulado |
| init | func init() | Inicializacion de paquete |
Ejercicios
- Crea una funcion
filtrar(nums []int, fn func(int) bool) []intque filtre numeros - Implementa un generador de Fibonacci usando closures
- Escribe una funcion variadic que reciba strings y retorne el mas largo
Resumen
- Go soporta retornos multiples, esencial para el patron
valor, err - Las funciones son valores de primera clase (se pasan y retornan)
- Los closures capturan variables del scope externo
...permite funciones variadic con N argumentos_descarta valores no necesariosinit()se ejecuta antes demain()para inicializacion