Vai al contenuto
eLearner.app
Modulo 3 · Lezione 4 di 514/50 nel corso~12 min
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:

Go
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:

Go
func counter() func() int {
    n := 0
    return func() int {
        n++
        return n
    }
}

c := counter()
fmt.Println(c(), c(), c())  // 1 2 3

n "vive" finché esiste almeno una closure che la referenzia. Ogni chiamata a counter() crea una nuova n indipendente.

Closure come callback

Pattern comunissimo:

Go
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:

Go
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:

Go
type Transform func(int) int
func apply(nums []int, f Transform) []int { ... }

Prova tu

Esercizio#go.m3.l4.e1
Tentativi: 0Caricamento…

Definisci counter() che ritorna una closure: ogni chiamata della closure incrementa e ritorna un counter interno.

Caricamento editor…
Mostra suggerimento

`n` va dichiarato PRIMA del return; la closure lo cattura.

Soluzione disponibile dopo 3 tentativi

Esercizio#go.m3.l4.e2
Tentativi: 0Caricamento…

Assegna a f una funzione letterale che stampa 'ciao', poi chiamala.

Caricamento editor…
Mostra suggerimento

Sintassi: `f := func() { ... }` poi `f()`.

Soluzione disponibile dopo 3 tentativi

Quiz#go.m3.l4.e3
Pronto

Cosa stampa? (attenzione allo scope delle closure)

Go
mk := func() func() int {
    x := 0
    return func() int { x++; return x }
}
a := mk()
b := mk()
fmt.Println(a(), a(), b())
Opzioni di risposta

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.