Go Nozioni di base sulla chiusura


Esempio

Una chiusura è una funzione presa insieme a un ambiente. La funzione è in genere una funzione anonima definita all'interno di un'altra funzione. L'ambiente è l'ambito lessicale della funzione di inclusione (l'idea di base di uno scope lessicale di una funzione sarebbe lo scopo che esiste tra le parentesi della funzione).

func g() {
    i := 0
    f := func() { // anonymous function
        fmt.Println("f called")
    }
}

All'interno del corpo di una funzione anonima (per esempio f ) definito all'interno di un'altra funzione (dire g ), le variabili presenti in ambiti sia f e g sono accessibili. Tuttavia, è lo scopo di g che forma la parte dell'ambiente della chiusura (la parte della funzione è f ) e di conseguenza le modifiche apportate alle variabili nello scope di g mantengono i loro valori (cioè l'ambiente persiste tra le chiamate a f ) .

Considera la seguente funzione:

func NaturalNumbers() func() int {
    i := 0
    f:= func() int { // f is the function part of closure
        i++
        return i
    }
    return f
}

In precedenza definizione, NaturalNumbers ha una funzione interna f che NaturalNumbers rendimenti. All'interno di f , si accede alla variabile i definita nell'ambito di NaturalNumbers .

Otteniamo una nuova funzione da NaturalNumbers modo:

n := NaturalNumbers()

Ora n è una chiusura. È una funzione (definita da f ) che ha anche un ambiente associato (ambito di NaturalNumbers ).

In caso di n , la parte ambiente contiene solo una variabile: i

Poiché n è una funzione, può essere chiamata:

fmt.Println(n()) // 1
fmt.Println(n()) // 2
fmt.Println(n()) // 3

Come evidente dall'output precedente, ogni volta che n viene chiamato, incrementa i . i inizia a 0, e ogni chiamata di n li esegue i++ .

Il valore di i viene mantenuto tra le chiamate. Cioè, l'ambiente, essendo parte della chiusura, persiste.

Chiamando di nuovo NaturalNumbers creerebbe e restituirà una nuova funzione. Ciò inizializzerebbe una nuova i entro NaturalNumbers . Il che significa che le forme funzionali di recente restituiti un'altra chiusura avere la stessa parte per la funzione (ancora f ), ma un ambiente nuovo di zecca (una nuova inizializzato i ).

o := NaturalNumbers()

fmt.Println(n()) // 4
fmt.Println(o()) // 1
fmt.Println(o()) // 2
fmt.Println(n()) // 5

Sia n che o sono chiusure contenenti la stessa parte di funzione (che dà loro lo stesso comportamento), ma ambienti diversi. Pertanto, l'uso di chiusure consente alle funzioni di accedere a un ambiente persistente che può essere utilizzato per conservare le informazioni tra le chiamate.

Un altro esempio:

func multiples(i int) func() int {
    var x int = 0
    return func() int {
        x++
        // paramenter to multiples (here it is i) also forms
        // a part of the environment, and is retained
        return x * i
    }
}

two := multiples(2)
fmt.Println(two(), two(), two()) // 2 4 6

fortyTwo := multiples(42)
fmt.Println(fortyTwo(), fortyTwo(), fortyTwo()) // 42 84 126