← Volver al listado de tecnologías

Funciones

Por: Artiko
gofuncionesclosuresvariadic

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:

  1. Variables a nivel de paquete
  2. Funciones init() (en orden de aparicion)
  3. main() (solo en package 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

TipoSintaxisUso
Basicafunc nombre(args) retFuncion estandar
Retorno multiplefunc f() (int, error)Manejo de errores
Named returnfunc f() (x, y int)Documentar retornos
Variadicfunc f(args ...int)N argumentos del mismo tipo
Anonimafunc(x int) int { ... }Callbacks, closures
ClosureAnonima que captura scopeEstado encapsulado
initfunc init()Inicializacion de paquete

Ejercicios

  1. Crea una funcion filtrar(nums []int, fn func(int) bool) []int que filtre numeros
  2. Implementa un generador de Fibonacci usando closures
  3. Escribe una funcion variadic que reciba strings y retorne el mas largo

Resumen