DMarlon Studio

Lección 7 · 7 de 20

Closures y funciones como valores · Go

Use funciones como valores en Go: tipos de función, funciones que devuelven closures y closures con estado propio. Reglas de precio y facturas.

Go · Closures y funciones como valores en DMarlon Studio: el código del ejemplo y su salida en la terminal

En Go las funciones son valores: se guardan en variables, se pasan como argumentos y se devuelven. PriceRule es un tipo de función: cualquier func(float64) float64 encaja, y eso permite tener una lista de reglas, rules, y aplicarlas una tras otra.

percentOff(10) devuelve una función anónima que usa pct, un parámetro de percentOff. Esa función es un closure: recuerda las variables del sitio donde nació aunque percentOff ya haya terminado. percentOff(10) y percentOff(5) son dos reglas distintas, cada una con su pct.

addVAT es una función literal guardada en una variable. No hace falta declararla como PriceRule: su tipo es func(float64) float64 y se puede poner en la lista tal cual. El bucle interior aplica las tres reglas en orden: 100 menos un 10 %, menos un 5 % y más el 21 % de IVA da 103.45.

invoiceNumbers devuelve un closure que modifica n. La variable no se pierde al volver de la función: vive mientras viva el closure, y cada llamada a next() la incrementa (INV-001, INV-002…). Es una forma sencilla de tener estado sin un struct.

Cada llamada a invoiceNumbers crea una n nueva: other empieza en REF-001 mientras next sigue por INV-004. Los argumentos de fmt.Println se evalúan de izquierda a derecha, por eso salen REF-001 y REF-002 en ese orden.

Código · main.go

package mainimport "fmt"// PriceRule is a function type: any func(float64) float64 fits.type PriceRule func(float64) float64func percentOff(pct float64) PriceRule {	return func(price float64) float64 { return price * (1 - pct/100) } // remembers pct}// invoiceNumbers returns a closure with its own counter, kept between calls.func invoiceNumbers(prefix string) func() string {	n := 0	return func() string {		n++		return fmt.Sprintf("%s-%03d", prefix, n)	}}func main() {	addVAT := func(price float64) float64 { return price * 1.21 } // a function value	rules := []PriceRule{percentOff(10), percentOff(5), addVAT}	next := invoiceNumbers("INV")	for _, price := range []float64{100, 40, 25} {		final := price		for _, apply := range rules {			final = apply(final)		}		fmt.Printf("%s  %6.2f -> %6.2f\n", next(), price, final)	}	other := invoiceNumbers("REF") // a new closure starts its own count	fmt.Println(other(), other(), "| main series goes on:", next())}

Salida · go run main.go

INV-001  100.00 -> 103.45
INV-002   40.00 ->  41.38
INV-003   25.00 ->  25.86
REF-001 REF-002 | main series goes on: INV-004

Para recordar

  • Las funciones son valores: se guardan, se pasan y se devuelven.
  • Un tipo de función, como PriceRule, da nombre a una firma.
  • Un closure recuerda las variables del sitio donde se creó.
  • Las variables capturadas sobreviven mientras viva el closure.
  • Cada llamada a la función externa crea un estado nuevo.

Ejercicio

Escriba minus(amount float64) PriceRule, que reste un importe fijo sin bajar de cero, y añada minus(5) a rules antes del IVA. Compruebe cómo cambia el total de cada factura.