Lezioni del modulo (4/5)
Closure e funzioni come valori
In Go le funzioni sono first-class values: si possono assegnare a variabili, passare ad altre funzioni e ritornarle. Una closure è una funzione letterale (anonima) che cattura le variabili dell'ambiente in cui è definita.
Funzione letterale
Una funzione senza nome, scritta inline. Si chiama subito o si assegna:
greet := func(name string) {
fmt.Println("ciao", name)
}
greet("Anna")
// invocata immediatamente:
func() { fmt.Println("subito") }()Il tipo di greet è func(string).
Catturare l'ambiente: closure
La funzione letterale può leggere e modificare le variabili visibili nel suo scope lessicale, anche dopo che la funzione esterna è tornata:
func counter() func() int {
n := 0
return func() int {
n++
return n
}
}
c := counter()
fmt.Println(c(), c(), c()) // 1 2 3n "vive" finché esiste almeno una closure che la referenzia. Ogni
chiamata a counter() crea una nuova n indipendente.
Closure come callback
Pattern comunissimo:
nums := []int{3, 1, 2}
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})La closure cattura nums e definisce l'ordinamento.
Funzioni come parametri
Per accettare una funzione come parametro, scrivi il suo tipo per esteso:
func apply(nums []int, f func(int) int) []int {
out := make([]int, len(nums))
for i, n := range nums {
out[i] = f(n)
}
return out
}
double := func(x int) int { return x * 2 }
apply([]int{1, 2, 3}, double) // [2 4 6]Se la firma diventa illeggibile, definisci un type alias:
type Transform func(int) int
func apply(nums []int, f Transform) []int { ... }Prova tu
Definisci counter() che ritorna una closure: ogni chiamata della closure incrementa e ritorna un counter interno.
Mostra suggerimento
`n` va dichiarato PRIMA del return; la closure lo cattura.
Soluzione disponibile dopo 3 tentativi
Assegna a f una funzione letterale che stampa 'ciao', poi chiamala.
Mostra suggerimento
Sintassi: `f := func() { ... }` poi `f()`.
Soluzione disponibile dopo 3 tentativi
Cosa stampa? (attenzione allo scope delle closure)
mk := func() func() int {
x := 0
return func() int { x++; return x }
}
a := mk()
b := mk()
fmt.Println(a(), a(), b())Recap
- Le funzioni sono first-class: assegnabili, passabili, ritornabili.
- Funzione letterale:
func(params) ret { ... }, anonima. - Closure: cattura le variabili dello scope lessicale, le tiene in vita.
- Tipo funzione esplicito nei parametri:
func(int) int. - Da Go 1.22 ogni iterazione for ha la sua copia della loop variable.