Go Les bases de la fermeture


Exemple

Une fermeture est une fonction associée à un environnement. La fonction est généralement une fonction anonyme définie dans une autre fonction. L'environnement est la portée lexicale de la fonction englobante (l'idée fondamentale d'une portée lexicale d'une fonction serait la portée qui existe entre les accolades de la fonction.)

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

Dans le corps d'une fonction anonyme (disons f ) définie dans une autre fonction (disons g ), les variables présentes dans les portées de f et de g sont accessibles. Cependant, c'est la portée de g qui constitue la partie de l'environnement de la fermeture (la partie fonction est f ) et, par conséquent, les modifications apportées aux variables de la portée de g conservent leurs valeurs (l'environnement persiste entre les appels à f ) .

Considérons la fonction ci-dessous:

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

Dans la définition ci-dessus, NaturalNumbers a une fonction interne f que retourne NaturalNumbers . À l'intérieur de f , la variable i définie dans le cadre de NaturalNumbers est en cours d'accès.

Nous obtenons une nouvelle fonction de NaturalNumbers comme ceci:

n := NaturalNumbers()

Maintenant n est une fermeture. C'est une fonction (définie par f ) qui possède également un environnement associé (portée de NaturalNumbers ).

Dans le cas de n , la partie environnement ne contient qu’une variable: i

Puisque n est une fonction, on peut l'appeler:

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

Comme il ressort de la sortie ci-dessus, chaque fois que n est appelé, il incrémente i . i commence à 0 et chaque appel à n exécute i++ .

La valeur de i est conservée entre les appels. En d'autres termes, l'environnement, qui fait partie de la fermeture, persiste.

Appeler NaturalNumbers nouveau créerait et renverrait une nouvelle fonction. Cela initialiserait un nouveau i dans NaturalNumbers . Ce qui signifie que la fonction nouvellement retournée forme une autre fermeture ayant le même rôle pour la fonction (toujours f ) mais un nouvel environnement (un i nouvellement initialisé).

o := NaturalNumbers()

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

Les deux n et o sont des fermetures contenant la même partie de fonction (ce qui leur donne le même comportement), mais des environnements différents. Ainsi, l'utilisation des fermetures permet aux fonctions d'accéder à un environnement persistant pouvant être utilisé pour conserver les informations entre les appels.

Un autre exemple:

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