Outils pour utilisateurs

Outils du site


cours:informatique:dev:golang:demarrer_avec_go:215_champ_application_variable

Notes et transcriptions du cours “Démarrer avec Go” proposée par University of California, Irvine disponible sur la plateforme coursera.

Portée des variables (scope)

Nous allons donc parler de la portée d'une variable. En gros, la portée d'une variable correspond aux endroits du code où elle est accessible.

Ainsi, la portée de la variable définit comment une variable est résolue dans le code. Si vous faites référence à une variable x, comment le programme détermine-t-il de quelle variable il s'agit réellement ?

C'est essentiellement ce qu'est la portée des variables (scope). Ces petits exemples illustrent un exemple de portée.

var x = 4
 
func f() {
   fmt.Printf("%d", x)
}
 
func g() {
  fmt.Printf("%d", x)
}
func f() {
   var x = 4
   fmt.Printf("%d", x)
}
 
func g() {
  fmt.Printf("%d", x)
}

Si nous regardons le premier bloc de code à gauche, nous avons une variable x. Cette variable x est définie en dehors de ces deux fonctions f() et g(). Ces fonctions sont très simples, elles affichent x.

Lorsque vous appelez ces fonctions, elles doivent donc déterminer où trouver la valeur de x pour l'afficher. Dans ce premier exemple, elles vont trouver la valeur définie à l'extérieur, là où est écrit var x = 4, elles vont afficher 4 car les règles de portée le permettent.

Donc, en gros, comme x est définit en dehors de l'une ou l'autre de ces fonctions, les deux y auront accès. Nous allons définir les règles formelles de portée dans quelques un peu plus tard.

Dans l'extrait de code à droite, vous allez avoir un problème. Il contient à nouveau les fonctions f() et g(), mais cette fois, la variable x est définie dans la fonction f(), mais pas dans la fonction g(). Donc, la fonction f l'affichera correctement car elle examinera x et la résoudra puisqu'elle est définie localement. Mais la fonction g() n'aura aucune référence à x. Elle ne pourra pas la voir et elle générera une erreur lorsque vous essaierez de l'exécuter, car elle ne saura pas ce qu'est x.

C'est donc le type de problème qui doit être résolu. Vous voulez être en mesure de savoir comment vos variables sont résolues pour éviter ce type de problème.

Nous allons donc parler de la portée des variables dès maintenant : comment le compilateur détermine-t-il comment une référence de variable doit être résolue ? De quel x parlez-vous ? Ce x ou bien cet autre x.

Ainsi, dans Go, la portée des variables se fait à l'aide de blocs. Le bloc est une séquence de déclarations et d'instructions entre crochets {}. Tout ce qui se trouve entre crochets s'appelle un bloc. C'est ainsi que vous définissez explicitement les blocs.

Notons que ces blocs peuvent être hiérarchiques. Vous pouvez avoir des crochets, puis à l'intérieur de ceux-ci, vous pouvez avoir d'autres crochets et à l'intérieur de ceux-ci, vous pouvez en avoir d'autres etc. Vous pouvez donc avoir cette hiérarchie de blocs. Il s'agit de blocs explicites.

Lorsque vous placez les crochets dans votre propre code, il s'agit de blocs explicites que vous incluez en tant que programmeur. Notons que les définitions de fonctions utilisent des crochets. Nous n'en sommes pas encore aux fonctions, nous en reparlerons plus tard mais pour chaque définition de fonction, vous donnez le nom de la fonction, puis vous avez des crochets ouverts, des crochets fermés. Il existe donc une hiérarchie de ces crochets et une hiérarchie de ces blocs.

Il existe également des blocs implicites dans cette hiérarchie, des blocs définis implicitement sans les crochets. Listons ces blocs :

  • Il y a d'abord le bloc “univers”. C'est tout le code source de Go, c'est le plus gros bloc, le bloc univers.
  • Il y a un bloc associé au package. Ainsi, pour chaque package, vous ne mettez pas entre crochets le code source d'un package en particulier, mais tout cela se trouve dans un seul bloc, qui se trouve à l'intérieur du bloc univers.
  • Ensuite, il y a le bloc de fichier : tout le code source d'un fichier se trouve dans le bloc de fichiers, et n'oubliez pas que le package peut être composé de beaucoup fichiers. Ainsi, il peut y avoir un bloc de package contenant plusieurs blocs de fichiers si vous avez beaucoup de fichiers dans le package.
  • D' autres blocs implicites incluent les instructions if, for, switch et select. Tous ces éléments ont des crochets qui définissent leurs propres blocs. Nous y reviendrons plus en détail ultérieurement.

Ces blocs implicites peuvent être utilisés de façon explicite. Par exemple, pour une instruction if, vous pouvez utiliser les crochets. Mais comme le bloc univers, le bloc package, le bloc fichier, ce sont tous des blocs implicites et il existe une hiérarchie entre eux.

Il existe une hiérarchie de ces blocs et chaque bloc peut avoir ses propres variables d'environnement associées.

Portée lexicale

La portée lexicale définit la manière dont les références aux variables sont résolues. Go est un langage à portée lexicale utilisant les blocs.

Ainsi, lorsque nous parlons de délimitation lexicale, nous devons parler de cette relation entre un bloc défini à l'intérieur d'un autre bloc.

On utilise la terminologie :

b_i >= b_j

  • b désigne un bloc.
  • Si b_j est défini dans b_i, alors b_i est supérieur ou égal à b_j.

Comme b_j est définit dans b_i on désigne b_j de portée interne (inner scope), et on désigne portée externe b_i outer scope).

Il s'agit d'une relation transitive. A titre d'exemple, regardez le code ci-dessus.

Nous avons la variable x (en rouge) initialisée à 4. Ensuite, nos avons une fonction f() et une fonction g(). Examinons à présent l'imbrication des blocs. Le code se trouve dans un seul fichier. Donc, tout cela se trouve dans le bloc de fichiers et je l'appelle b1. En plus du bloc de fichiers, je définis deux fonctions, f() et g(). Chacun de ces blocs fonctionnels possède son propre bloc, donc b2 est le bloc fonctionnel pour f() et b3 est le bloc fonctionnel pour g().

Si je devais regarder comment ces blocs sont liés : b2 et b3 sont tous deux définis dans b1. Donc, je dis que b1 est supérieur à b2, et b1 est supérieur à b3 selon ma définition, car b2 et b3 sont définis à l'intérieur de b1. Mais notez qu'il n'y a aucune relation entre b2 et b3. Parce qu'ils ne sont pas définis l'un dans l'autre.

Nous devons donc en prendre conscience, car selon les règles de portée, lorsque vous résolvez une variable, vous passez à la portée la plus étendue.

Ainsi, dans l' exemple, la fonction f() se trouve le bloc b1 avec la variable x. Lors de l’exécution de f() la variable x va être recherchée dans le bloc lui-même (b2 portée locale). Mais comme x n'existe pas, elle est recherchée dans le bloc de plus haut niveau (ici b1). Donc, on commence par rechercher x dans b2, si elle n'est pas définie, on consulte le bloc b1 : comme la définition de x existe bien dans b1, on utilise celle-ci.

Il en va de même pour b3. Si vous regardez la fonction g(), elle utilise la variable x, elle accède à la variable x définie dans b1. Elle regarde d'abord dans son bloc local (b3), elle ne la voit pas. Donc, elle regarde à l'intérieur du bloc supérieur b1 et la trouve ici. C' est pourquoi ce code fonctionnera : la variable x sera résolue correctement.

Ainsi, lorsque vous parlez de portée de variable, une variable est accessible depuis un bloc b_j, si les variables sont déclarées dans un bloc b_i, et le bloc b_i est supérieur/égal à b_j.

Donc, ce sont soit les variables que vous avez déclarées dans b_j, soit elles sont déclarées dans un bloc extérieur et supérieur à b_j.

Les deux fonctions, qui se trouvent également dans le même bloc de fichiers, peuvent toutes deux accéder correctement à la variable x car leurs blocs de fonctions se trouvent dans le bloc de fichier.

Mais dans le bloc de code ci dessous

func f() {
   var x = 4
   fmt.Printf("%d", x)
}
 
func g() {
  fmt.Printf("%d", x)
}

la variable x est définie dans le bloc fonctionnel de f(). Elle n'est pas dans le bloc fonctionnel de g. Ainsi, lorsque g() essaie de trouver x, la variable x, n'estp as présente dans son bloc local. Il ne le voit pas non plus dans le bloc supérieur de fichiers, car la définition se trouve maintenant dans le bloc de fonction de la fonction f(). C'est pourquoi cela échoue, x n'est pas résolu en quoi que ce soit et il y a une erreur.

◁ Précédent | ⌂ Retour au sommaire | Suivant ▷

cours/informatique/dev/golang/demarrer_avec_go/215_champ_application_variable.txt · Dernière modification : 2024/05/11 11:52 de yoann