Article de reference

Portée (programmation informatique)

( Apprenez comment et quand supprimer ce message ) En programmation , la portée d'une association de nom (l'association d'un nom à une entité, comme une variable ) correspond à ...

programmation , la portée d'une association de nom (l'association d'un nom à une entité, comme une variable ) correspond à la partie du programme où cette association est valide. Autrement dit, la portée d'un nom est l'endroit où il peut être utilisé pour désigner une entité . Ailleurs dans le programme, le nom peut désigner une autre entité (il peut avoir une autre association), ou ne désigner rien du tout (il peut ne pas être associé). La portée permet d'éviter les conflits de noms en autorisant un même nom à désigner différents objets, à condition que ces noms aient des portées distinctes. La portée d'une association de nom est également appelée visibilité d'une entité, notamment dans la documentation plus ancienne ou plus technique ; cette notion se rapporte à l'entité référencée, et non au nom qui la référence.

Le terme « portée » est également utilisé pour désigner l'ensemble de toutes les liaisons de noms valides au sein d'une partie d'un programme ou à un point donné d'un programme, ce qui est plus correctement appelé contexte ou environnement .

parler et en pratique pour la plupart des langages de programmation , l' « partie d'un programme » désigne une portion de code source (une zone de texte) et est appelée portée lexicale . Dans certains langages, cependant, elle désigne une portion du temps d'exécution (une période durant laquelle le programme est exécuté ) et est appelée portée dynamique . Ces deux termes peuvent prêter à confusion – ils utilisent des termes techniques de manière inappropriée, comme expliqué dans la définition – mais la distinction elle-même est exacte et précise, et il s'agit des termes standard respectifs. Cet article se concentre principalement sur la portée lexicale, la portée dynamique étant comprise par opposition à la portée lexicale.

Dans la plupart des cas, la résolution de noms basée sur la portée lexicale est relativement simple à utiliser et à implémenter : à l’usage, il suffit de remonter dans le code source pour déterminer à quelle entité un nom se réfère ; à l’ implémentation, on peut maintenir une liste des noms et des contextes lors de la compilation ou de l’interprétation d’un programme. Des difficultés apparaissent avec le masquage de noms , les déclarations anticipées et le hoisting , tandis que des difficultés bien plus subtiles se posent avec les variables non locales , notamment dans les fermetures .

identificateur ) est sans ambiguïté : la portée lexicale est « la portion du code source dans laquelle s'applique la liaison d'un nom à une entité ». Cette définition est pratiquement inchangée depuis 1960, date de la spécification d' ALGOL 60. Voici quelques spécifications de langages représentatifs :

ALGOL 60 (1960)
On distingue les types de quantités suivants : variables simples, tableaux, étiquettes, instructions conditionnelles et procédures. La portée d’une quantité correspond à l’ensemble des instructions et expressions dans lesquelles la déclaration de l’identificateur associé à cette quantité est valide.
C (2007)
Un identificateur peut désigner un objet ; une fonction ; une étiquette ou un membre d’une structure, d’une union ou d’une énumération ; un nom de type ; un nom d’étiquette ; un nom de macro ; ou un paramètre de macro. Un même identificateur peut désigner différentes entités à différents endroits du programme. [...] Pour chaque entité différente désignée par un identificateur, celui-ci n’est visible (c’est-à-dire utilisable) que dans une zone du texte du programme appelée sa portée.
Go (2013)
Une déclaration associe un identificateur non vide à une constante, un type, une variable, une fonction, une étiquette ou un paquetage. [...] La portée d'un identificateur déclaré correspond à l'étendue du texte source dans laquelle cet identificateur désigne la constante, le type, la variable, la fonction, l'étiquette ou le paquetage spécifié.

Le terme « portée » désigne le plus souvent le cas où un nom donné peut faire référence à une variable donnée — lorsqu'une déclaration a un effet — mais il peut également s'appliquer à d'autres entités, telles que des fonctions, des types, des classes, des étiquettes , des constantes et des énumérations.

Portée lexicale vs. portée dynamique

Une distinction fondamentale en matière de portée réside dans la définition de « partie d'un programme ». Dans les langages à portée lexicale (ou statique ), la résolution des noms dépend de leur emplacement dans le code source et du contexte lexical (ou statique ), défini par l'endroit où la variable ou la fonction nommée est définie. En revanche, dans les langages à portée dynamique, la résolution des noms dépend de l' état du programme au moment où le nom est rencontré, état déterminé par le contexte d'exécution ( ou contexte d' appel ). Concrètement, avec une portée lexicale , la résolution d'un nom s'effectue en recherchant dans le contexte lexical local, puis, en cas d'échec, dans le contexte lexical externe, et ainsi de suite. Avec une portée dynamique, la résolution s'effectue en recherchant dans le contexte d'exécution local, puis, en cas d'échec, dans le contexte d'exécution externe, et ainsi de suite, en remontant la pile d'appels.

La plupart des langages modernes utilisent la portée lexicale pour les variables et les fonctions, bien que la portée dynamique soit utilisée dans certains langages, notamment certains dialectes de Lisp, certains langages de script et certains langages à modèles . Perl 5 offre à la fois la portée lexicale et la portée dynamique. Les fonctions qui utilisent des variables à portée lexicale sont appelées fermetures .

La résolution lexicale peut être déterminée au moment de la compilation et est également connue sous le nom de liaison précoce , tandis que la résolution dynamique ne peut en général être déterminée qu'au moment de l'exécution et est donc connue sous le nom de liaison tardive .

Concepts connexes

En programmation orientée objet , la répartition dynamique sélectionne une méthode d'objet à l'exécution, mais le moment de la liaison des noms (compilation ou exécution) dépend du langage. La portée dynamique de facto est courante dans les langages de macros , qui n'effectuent pas directement la résolution des noms, mais procèdent à une expansion sur place.

Certains frameworks de programmation, comme AngularJS, utilisent le terme « scope » dans un sens syntaxique différent de celui employé dans cet article. Dans ces frameworks, le scope est simplement un objet du langage de programmation utilisé ( JavaScript dans le cas d'AngularJS) qui sert à émuler la portée dynamique dans un langage utilisant la portée lexicale pour ses variables. Ces scopes AngularJS peuvent être contextuels ou non (au sens habituel du terme) dans n'importe quelle partie du programme, suivant les règles de portée des variables du langage comme tout autre objet, et utilisant leurs propres règles d'héritage et de transclusion . Dans le contexte d'AngularJS, le terme « $scope » (avec un signe dollar) est parfois utilisé pour éviter toute confusion, mais l'utilisation du signe dollar dans les noms de variables est généralement déconseillée par les guides de style.

Utiliser

La portée est un élément important de la résolution de noms , elle-même fondamentale pour la sémantique du langage . La résolution de noms (y compris la portée) varie d'un langage de programmation à l'autre et, au sein d'un même langage, selon le type d'entité ; les règles de portée sont appelées règles de portée . Avec les espaces de noms , les règles de portée sont essentielles à la programmation modulaire , car une modification dans une partie du programme n'affecte pas les autres parties.

Aperçu

lexical context or static context) or a portion of run time (execution context,runtime context,calling context or dynamic context). Execution context consists of lexical context (at the current execution point) plus additional runtime state such as the call stack. Strictly speaking, during execution a program enters and exits various name bindings' scopes, and at a point in execution name bindings are "in context" or "not in context", hence name bindings "come into context" or "go out of context" as the program execution enters or exits the scope. However, in practice usage is much looser.

Scope is a source-code level concept, and a property of name bindings, particularly variable or function name bindings—names in the source code are references to entities in the program—and is part of the behavior of a compiler or interpreter of a language. As such, issues of scope are similar to pointers, which are a type of reference used in programs more generally. Using the value of a variable when the name is in context but the variable is uninitialized is analogous to dereferencing (accessing the value of) a wild pointer, as it is undefined. However, as variables are not destroyed until they go out of context, the analog of a dangling pointer does not exist.

Pour les entités telles que les variables, la portée est un sous-ensemble de la durée de vie (ou étendue ) : un nom ne peut désigner qu'une variable existante (éventuellement avec une valeur indéfinie), mais les variables existantes ne sont pas nécessairement visibles : une variable peut exister mais être inaccessible (sa valeur est stockée mais non référencée dans un contexte donné), ou accessible mais pas via son nom, auquel cas elle n'est pas dans le contexte (le programme est « hors de la portée du nom »). Dans d'autres cas, la « durée de vie » est sans importance : une étiquette (position nommée dans le code source) a une durée de vie identique à celle du programme (pour les langages compilés statiquement), mais peut être dans le contexte ou non à un moment donné du programme. Il en va de même pour les variables statiques : une variable globale statique est dans le contexte pour l'ensemble du programme, tandis qu'une variable locale statique n'est dans le contexte que dans une fonction ou un autre contexte local, mais toutes deux ont une durée de vie correspondant à l'exécution complète du programme.

Déterminer à quelle entité un nom fait référence est appelé résolution de noms ou liaison de noms (notamment en programmation orientée objet ), et varie selon les langages. Étant donné un nom, le langage (plus précisément, le compilateur ou l'interpréteur) recherche des correspondances parmi toutes les entités présentes dans le contexte. En cas d'ambiguïté (deux entités portant le même nom, comme une variable globale et une variable locale), les règles de résolution de noms permettent de les distinguer. Le plus souvent, la résolution de noms repose sur une règle de « contexte interne vers contexte externe », telle que la règle LEGB (Local, Enclosing, Global, Built-in) de Python : les noms sont implicitement résolus au contexte pertinent le plus restreint. Dans certains cas, la résolution de noms peut être spécifiée explicitement, par exemple avec les mots-clés `name` globalet nonlocal`const` en Python ; dans d'autres cas, les règles par défaut ne peuvent pas être modifiées.

Lorsque deux noms identiques apparaissent simultanément dans le même contexte, mais désignent des entités différentes, on parle de masquage de noms : le nom prioritaire (généralement le plus interne) « masque » le nom moins prioritaire. Au niveau des variables, ce phénomène est appelé masquage de variables . En raison des risques d’ erreurs logiques liés au masquage, certains langages l’interdisent ou le déconseillent, en générant une erreur ou un avertissement à la compilation ou à l’exécution.

Les différents langages de programmation appliquent des règles de portée différentes aux déclarations et aux noms. Ces règles ont une incidence majeure sur la sémantique du langage et, par conséquent, sur le comportement et la correction des programmes. Dans des langages comme C++ , l'accès à une variable non liée n'a pas de sémantique bien définie et peut entraîner un comportement indéfini , similaire à l'utilisation d'un pointeur invalide ; de plus, les déclarations ou les noms utilisés hors de leur portée génèrent des erreurs de syntaxe .

Les portées sont fréquemment liées à d'autres constructions du langage et déterminées implicitement, mais de nombreux langages offrent également des constructions spécifiques pour contrôler la portée.

Niveaux de portée

La portée peut varier d'une simple expression à l'ensemble du programme, avec de nombreuses nuances intermédiaires. La règle de portée la plus simple est la portée globale : toutes les entités sont visibles dans tout le programme. La règle de portée modulaire la plus élémentaire est la portée à deux niveaux : une portée globale, accessible partout dans le programme, et une portée locale, au sein d'une fonction. La programmation modulaire plus sophistiquée autorise une portée de module distincte, où les noms sont visibles à l'intérieur du module (privés au module) mais invisibles à l'extérieur. Au sein d'une fonction, certains langages, comme le C, permettent la portée de bloc, qui restreint la portée à un sous-ensemble de la fonction ; d'autres, notamment les langages de programmation fonctionnelle , permettent la portée d'expression, qui restreint la portée à une seule expression. Parmi les autres portées, on trouve la portée de fichier (notamment en C), qui se comporte de manière similaire à la portée de module, et la portée de bloc en dehors des fonctions (notamment en Perl).

Une question subtile concerne la définition précise du début et de la fin de la portée d'un nom. Dans certains langages, comme le C, la portée d'un nom commence à sa déclaration ; ainsi, différents noms déclarés au sein d'un même bloc peuvent avoir des portées différentes. Cela implique de déclarer les fonctions avant leur utilisation, sans nécessairement les définir, et requiert parfois une déclaration anticipée , notamment pour la récursivité mutuelle. Dans d'autres langages, comme Python, la portée d'un nom commence au début du bloc où il est déclaré (par exemple, au début d'une fonction), indépendamment de son emplacement de définition. Tous les noms au sein d'un même bloc ont donc la même portée. En JavaScript, la portée d'un nom déclaré avec `var` letou ` constvar` commence à sa déclaration, et celle d'un nom déclaré avec `var` varcommence au début de la fonction où il est déclaré ; c'est ce qu'on appelle la remontée de variables (ou hoistingvar ). Le comportement des noms non définis dans un contexte donné diffère : en Python, leur utilisation provoque une erreur d'exécution, tandis qu'en JavaScript, les noms non définis déclarés avec `var` sont utilisables dans toute la fonction car ils sont implicitement liés à la valeur ` undefined` undefined.

Portée de l'expression

La portée d'une liaison de nom est une expression , appelée portée d'expression . La portée d'expression est disponible dans de nombreux langages, notamment les langages fonctionnels qui offrent une fonctionnalité appelée expressions ` let` , permettant à la portée d'une déclaration d'être une expression unique . Ceci est pratique si, par exemple, une valeur intermédiaire est nécessaire pour un calcul. Par exemple , en Standard ML , si ` f()f ( ...12let val x = f() in x * x end144xf()prototype d'une fonction ont une portée d'expression, appelée ici portée de protocole de fonction . Comme ces noms de variables ne sont pas utilisés dans la définition (ils peuvent différer de la définition proprement dite) — ce sont simplement des variables fictives —, ils sont souvent omis, même s'ils peuvent servir à générer de la documentation, par exemple.

Portée du bloc

La portée d'une liaison de nom est un bloc , appelé portée de bloc . La portée de bloc est disponible dans de nombreux langages de programmation structurés par blocs, mais pas dans tous. Elle a été introduite avec ALGOL 60 , où « chaque déclaration… n'est valable que pour ce bloc » , et est aujourd'hui particulièrement associée aux langages des familles Pascal et C. Le plus souvent, ce bloc est contenu dans une fonction, limitant ainsi sa portée à une partie de celle-ci. Cependant, dans certains cas, comme en Perl, le bloc peut ne pas se trouver à l'intérieur d'une fonction.

boucle `while` peut être placée dans un bloc qui initialise les variables utilisées à l'intérieur de la boucle et qui ne doivent être initialisées qu'une seule fois.

Une subtilité de plusieurs langages de programmation, tels qu'Algol 68 et C (illustrée dans cet exemple et normalisée depuis C99 ), réside dans la possibilité de déclarer des variables de portée bloc non seulement au sein du corps du bloc, mais aussi dans l'instruction de contrôle, le cas échéant. Ceci est analogue aux paramètres de fonction, déclarés lors de la déclaration de la fonction (avant le début du corps de la fonction) et dont la portée s'étend sur toute la durée de ce corps. Cette pratique est principalement utilisée dans les boucles `for` , qui possèdent une instruction d'initialisation distincte de la condition de boucle, contrairement aux boucles `while`, et constitue une construction courante.

La portée de bloc permet de masquer des variables. Dans cet exemple, à l'intérieur du bloc, la variable auxiliaire aurait également pu être nommée `n` , masquant ainsi le nom du paramètre. Cependant, cette pratique est déconseillée en raison des risques d'erreurs. De plus, certains langages dérivés du C, comme Java et C#, bien que prenant en charge la portée de bloc (une variable locale pouvant être déclarée hors de son contexte avant la fin d'une fonction), n'autorisent pas le masquage d'une variable locale par une autre. Dans ces langages, la déclaration d'une seconde variable `n` entraînerait une erreur de syntaxe, et l'une des variables `n` devrait être renommée.

Si un bloc est utilisé pour définir la valeur d'une variable, la portée du bloc exige que la variable soit déclarée en dehors du bloc. Cela complique l'utilisation des instructions conditionnelles avec une seule affectation . Par exemple, en Python, qui n'utilise pas la portée de bloc, on peut initialiser une variable comme ceci :

sinon : a =

aest accessible après l' ifinstruction.

En Perl, qui utilise la portée de bloc, cela nécessite en revanche de déclarer la variable avant le bloc :

En Perl, cela donnerait :

opérateur ternaire pour éviter un bloc, mais cela n'est généralement pas possible pour les affectations de plusieurs variables et est difficile à lire pour une logique complexe.

C’est un problème plus important en C, notamment pour l’affectation de chaînes de caractères, car l’initialisation d’une chaîne peut allouer automatiquement de la mémoire, tandis que l’affectation d’une chaîne à une variable déjà initialisée nécessite l’allocation de mémoire, une copie de la chaîne et la vérification du succès de ces opérations.

variable locale dans une fonction ou une sous-routine : une variable dont la portée se termine (qui sort du contexte) lorsque la fonction se termine. Dans la plupart des cas, la durée de vie de la variable correspond à celle de l'appel de fonction ; il s'agit d'une variable automatique , créée au démarrage de la fonction (ou lors de sa déclaration) et détruite à la fin de son exécution. La portée de la variable est limitée à la fonction, bien que la signification de « à l'intérieur » dépende du type de portée (lexicale ou dynamique). Cependant, certains langages, comme le C, proposent également des variables locales statiques , dont la durée de vie correspond à celle du programme, mais qui ne sont accessibles au contexte que lorsqu'elles se trouvent à l'intérieur de la fonction. Dans le cas des variables locales statiques, la variable est créée à l'initialisation du programme et détruite à sa fin, comme une variable globale statique , mais son contexte est limité à l'intérieur de la fonction, à l'instar d'une variable locale automatique.

Il est important de noter que, dans la portée lexicale, une variable de portée fonctionnelle n'a de portée que dans le contexte lexical de la fonction : elle sort de son contexte lorsqu'une autre fonction est appelée à l'intérieur de celle-ci, et y revient lorsque la fonction se termine. Les fonctions appelées n'ont pas accès aux variables locales des fonctions appelantes, et les variables locales n'ont de contexte que dans le corps de la fonction où elles sont déclarées. En revanche, dans la portée dynamique, la portée s'étend au contexte d'exécution de la fonction : les variables locales restent dans leur contexte lorsqu'une autre fonction est appelée, et n'en sortent que lorsque la fonction les définissant se termine. Ainsi, les variables locales ont de contexte à la fois dans la fonction qui les définit et dans toutes les fonctions appelées . Dans les langages à portée lexicale et à fonctions imbriquées , les variables locales ont de contexte pour les fonctions imbriquées, car elles appartiennent au même contexte lexical, mais pas pour les autres fonctions non imbriquées lexicalement. Une variable locale d'une fonction englobante est dite non locale pour cette fonction imbriquée. La portée fonctionnelle s'applique également aux fonctions anonymes .

int: return n * n def sum_of_squares(n: int) -> int: total: int = 0 i: int = 0 while i <= n: total += square(i) i += 1 return total "
def carré ( n : int ) -> int : return n * ndef somme_des_carrés ( n : int ) -> int : total : int = 0 i : int = 0 while i <= n : total += carré ( i ) i += 1 return total

Par exemple, dans l'extrait de code Python à droite, deux fonctions sont définies : ` sq` squareet sum_of_squares`sum`. `sq` squarecalcule le carré d'un nombre ; ` sum_of_squaressum` calcule la somme de tous les carrés inférieurs ou égaux à un nombre donné. (Par exemple, ` 4²`square(4) vaut ` 4²` , et ` + 1² + + 3² + 4²` vaut `0² + + 2² + + 4²` . )16sum_of_squares(4)30

Chacune de ces fonctions possède une variable nommée `n` qui représente son argument. Ces deux variables `n` sont totalement distinctes et indépendantes, malgré leur nom identique, car ce sont des variables locales à portée lexicale limitée à la fonction : la portée de chacune est une fonction distincte, et elles ne se chevauchent donc pas. Par conséquent, sum_of_squareson peut appeler ` squaren` sans que sa propre variable ` n` soit modifiée. De même, ` sum_of_squaresn` possède des variables nommées `total` et `i` ; ces variables, du fait de leur portée limitée, n'interféreront pas avec d'autres variables nommées `total` ou `i` appartenant à d'autres fonctions. Autrement dit, il n'y a aucun risque de conflit de noms entre ces variables et d'autres variables non liées, même si elles sont identiques.

Aucun masquage de nom n'a lieu : une seule variable nommée n est accessible à un instant donné, car les portées ne se chevauchent pas. En revanche, si un fragment similaire était écrit dans un langage à portée dynamique, la variable n de la fonction appelante resterait accessible dans la fonction appelée (les portées se chevaucheraient) et serait masquée (« ombragée ») par la nouvelle variable n de la fonction appelée.

La portée des fonctions est nettement plus complexe si elles sont des objets de première classe, pouvant être créées localement par une fonction puis renvoyées. Dans ce cas, toute variable de la fonction imbriquée qui n'est pas locale à celle-ci (variables non liées dans la définition de la fonction, qui sont résolues en variables dans un contexte englobant) crée une fermeture , car non seulement la fonction elle-même, mais aussi son contexte (de variables) doivent être renvoyés, puis potentiellement appelés dans un contexte différent. Ceci exige une prise en charge bien plus importante de la part du compilateur et peut complexifier l'analyse du programme .

Portée du fichier

La portée d'une liaison de nom est limitée au fichier, ce qu'on appelle la portée de fichier . La portée de fichier est spécifique au C (et au C++), où la portée des variables et des fonctions déclarées à la racine d'un fichier (hors des fonctions elles-mêmes) s'étend à l'ensemble du fichier, ou plus précisément, en C, de la déclaration jusqu'à la fin du fichier source, ou plus précisément jusqu'à l'unité de traduction (liaison interne). On peut y voir une forme de portée de module, où les modules sont identifiés par des fichiers, et qui, dans les langages plus modernes, est remplacée par une notion explicite de portée de module. En raison de la présence d'instructions `include`, qui ajoutent des variables et des fonctions au contexte interne et peuvent elles-mêmes appeler d'autres instructions `include`, il peut être difficile de déterminer ce qui est inclus dans le contexte au sein du corps d'un fichier.

Dans l'extrait de code C ci-dessus, le nom de la fonction sum_of_squaresa une portée globale (en C, liaison externe). Ajouter un élément les langages de programmation modulaires où les modules (pouvant s'étendre sur plusieurs fichiers) constituent l'unité de base d'un programme complexe, car ils permettent de masquer des informations et d'exposer une interface limitée. La portée de module a été introduite pour la première fois dans la famille de langages Modula , et Python (influencé par Modula) en est un exemple contemporain représentatif.

Dans certains langages de programmation orientés objet ne prenant pas directement en charge les modules, comme le C++ avant C++20 , une structure similaire est fournie par la hiérarchie des classes. Les classes constituent l'unité de base du programme et peuvent contenir des méthodes privées. Ce principe s'applique davantage à la répartition dynamique des appels qu'à la résolution de noms et à la portée des méthodes, bien que ces deux aspects soient souvent analogues. Dans certains cas, ces deux mécanismes sont disponibles, comme en Python, qui propose à la fois des modules et des classes. L'organisation du code (sous forme de fonction de module ou de méthode privée conventionnelle) est alors laissée au choix du programmeur.

Portée mondiale

La portée d'une liaison de nom s'étend à l'ensemble du programme ; on parle alors de portée globale . L'utilisation de noms de variables à portée globale (appelées variables globales ) est souvent déconseillée, du moins dans certains langages, en raison des risques de conflits de noms et de masquage involontaire, ainsi que d'une faible modularité. La portée de fonction ou de bloc est généralement préférable. Cependant, la portée globale est couramment utilisée (selon le langage) pour d'autres types de noms, tels que les noms de fonctions, de classes et d'autres types de données . Dans ces cas, des mécanismes comme les espaces de noms permettent d'éviter les conflits.

Portée lexicale vs. portée dynamiqueen Bash , qui utilise la portée dynamique ; il affiche donc `f` 3puis `f` 1. Si le même code était exécuté avec ksh93 , qui utilise la portée lexicale, les résultats seraient différents.)

pile d'appels d'exécution par l'implémentation du langage. Puisque cette correspondance ne nécessite que l'analyse du texte statique du programme, ce type de portée est également appelé portée statique . La portée lexicale est standard dans tous les langages basés sur ALGOL, tels que Pascal , Modula-2 et Ada , ainsi que dans les langages fonctionnels modernes comme ML et Haskell . Elle est également utilisée en C et dans ses langages apparentés sur les plans syntaxique et sémantique, bien qu'avec des limitations différentes. La portée statique permet au programmeur de raisonner sur les références d'objets (paramètres, variables, constantes, types, fonctions, etc.) comme de simples substitutions de noms. Ceci facilite grandement la conception de code modulaire et son analyse, car la structure de nommage locale peut être comprise isolément. À l'inverse, la portée dynamique oblige le programmeur à anticiper tous les contextes d'exécution possibles dans lesquels le code du module peut être invoqué.

des fonctions imbriquées de première classe n'est pas triviale, car elle exige que chaque valeur de fonction contienne une trace des valeurs des variables dont elle dépend (la paire fonction-contexte est appelée fermeture ). Selon l'implémentation et l'architecture de l'ordinateur , la recherche de variables peut devenir légèrement inefficace lors de l'utilisation de fonctions très profondément imbriquées lexicalement , bien qu'il existe des techniques connues pour atténuer ce problème. De plus, pour les fonctions imbriquées qui ne font référence qu'à leurs propres arguments et aux variables (immédiatement) locales, toutes les positions relatives sont connues à la compilation . L'utilisation de ce type de fonction imbriquée n'entraîne donc aucune surcharge. Il en va de même pour les parties d'un programme où les fonctions imbriquées ne sont pas utilisées, et, naturellement, pour les programmes écrits dans un langage où les fonctions imbriquées ne sont pas disponibles (comme en C).

Histoire

La portée lexicale a été utilisée pour la première fois au début des années 1960 pour le langage impératif ALGOL 60 et a été reprise dans la plupart des autres langages impératifs depuis lors.

Les langages comme Pascal et C ont toujours eu une portée lexicale, puisqu'ils sont tous deux influencés par les idées qui ont conduit à ALGOL 60 et ALGOL 68 (bien que C n'ait pas inclus de fonctions imbriquées lexicalement ).

Perl est un langage à portée dynamique auquel la portée statique a été ajoutée ultérieurement.L'interpréteur Lisp original (1960) utilisait une portée dynamique. La liaison profonde , qui se rapproche de la portée statique (lexicale), a été introduite vers 1962 dans LISP 1.5 (via le dispositif Funarg développé par Steve Russell , travaillant sous la direction de John McCarthy ).

Tous les premiers Lisp utilisaient la portée dynamique lorsqu'ils étaient basés sur des interpréteurs. En 1982, Guy L. Steele Jr. et le Common Lisp Group ont publié *An overview of Common Lisp * , un bref aperçu de l'histoire et des différentes implémentations de Lisp jusqu'alors, ainsi qu'une description des caractéristiques que devrait posséder une implémentation de Common Lisp . À la page 102, on peut lire :

La plupart des implémentations LISP présentent une incohérence interne : par défaut, l’interpréteur et le compilateur peuvent attribuer des sémantiques différentes à des programmes corrects. Cela provient principalement du fait que l’interpréteur considère toutes les variables comme ayant une portée dynamique, tandis que le compilateur les considère comme locales, sauf indication contraire. Ce choix, motivé par des raisons de commodité et d’efficacité, peut engendrer des bogues très subtils. La définition de Common LISP évite ces anomalies en exigeant explicitement que l’interpréteur et le compilateur imposent une sémantique identique aux programmes corrects.

Les implémentations de Common Lisp devaient donc avoir une portée lexicale . (Voir aussi : « Présentation de Common Lisp » ).

De plus, Common LISP offre les fonctionnalités suivantes (dont la plupart sont empruntées à MacLisp, InterLisp ou Lisp Machines Lisp) : (...) Variables à portée lexicale complète. Le problème dit « FUNARG » est entièrement résolu, aussi bien dans le cas descendant que dans le cas ascendant.

La même année que la publication de *An overview of Common LISP* (1982), les premières ébauches (également de Guy L. Steele Jr.) d'un Lisp compilé à portée lexicale, appelé Scheme , avaient été publiées et des implémentations de compilateurs étaient en cours d'élaboration. À cette époque, on craignait généralement que la portée lexicale en Lisp soit difficile à implémenter. Dans *A History of T* , Olin Shivers écrit :

Tous les Lisp sérieusement utilisés en production à cette époque étaient à portée dynamique. Personne, hormis ceux qui avaient lu attentivement la thèse Rabbit (écrite par Guy Lewis Steele Jr. en 1978), ne croyait au succès de la portée lexicale ; même les rares personnes qui l’ avaient lue faisaient preuve d’une certaine audace quant à son fonctionnement en production.

Le terme « portée lexicale » remonte au moins à 1967, tandis que le terme « portée lexicale » remonte au moins à 1970, où il a été utilisé dans le cadre du projet MAC pour décrire les règles de portée du dialecte Lisp MDL (alors connu sous le nom de « Muddle »).

Portée lexicale en programmation moderne

Dans les langages de programmation modernes, la portée lexicale joue un rôle crucial dans la mise en œuvre des paradigmes de programmation fonctionnelle. Des langages comme JavaScript , Python et Swift s'appuient fortement sur la portée lexicale pour garantir que les fonctions puissent accéder aux variables de leur contexte de définition, même si elles sont exécutées en dehors de cette portée. Ceci est particulièrement important pour les fermetures, qui découlent directement de la portée lexicale.

Par exemple, en JavaScript, les fermetures sont fréquemment utilisées pour la programmation asynchrone et la gestion d'événements, car elles permettent aux fonctions de rappel de conserver l'accès aux variables de la portée extérieure, ce qui facilite un code plus propre et plus modulaire. De même, Python et Swift utilisent la portée lexicale pour implémenter des fermetures et permettre des modèles puissants comme les fonctions d'ordre supérieur.

Portée dynamique

Avec la portée dynamique , un nom fait référence à un contexte d'exécution. Techniquement, cela signifie que chaque nom possède une pile globale de liaisons. L'introduction d'une variable locale nommée xajoute une liaison à la pile globale x(qui peut être vide au départ), laquelle est dépilée lorsque le flux d'exécution quitte la portée. L'évaluation xdans n'importe quel contexte renvoie toujours la liaison la plus récente. Notez que cela ne peut pas être fait à la compilation, car la pile de liaisons n'existe qu'à l'exécution ; c'est pourquoi ce type de portée est appelé portée dynamique .

La portée dynamique est rare dans les langages modernes.

Généralement, certains blocs sont définis pour créer des liaisons dont la durée de vie correspond à la durée d'exécution du bloc ; cela ajoute certaines caractéristiques de la portée statique au processus de portée dynamique. Cependant, comme une section de code peut être appelée depuis de nombreux endroits et dans diverses situations, il peut être difficile de déterminer d'emblée quelles liaisons s'appliqueront lorsqu'une variable est utilisée (voire même si une liaison existe). Cela peut s'avérer avantageux ; l'application du principe de moindre connaissance suggère que le code évite de dépendre des raisons (ou des circonstances) de la valeur d'une variable, mais utilise simplement la valeur conformément à sa définition. Cette interprétation restrictive des données partagées peut fournir un système très flexible pour adapter le comportement d'une fonction à l'état (ou à la politique) actuel du système. Toutefois, cet avantage repose sur une documentation rigoureuse de toutes les variables utilisées de cette manière et sur une évitement strict des suppositions concernant le comportement d'une variable, et ne fournit aucun mécanisme pour détecter les interférences entre différentes parties d'un programme. Certains langages, comme Perl et Common Lisp , permettent au programmeur de choisir entre une portée statique ou dynamique lors de la définition ou de la redéfinition d'une variable. Parmi les langages utilisant la portée dynamique, on peut citer Logo , Emacs Lisp , LaTeX et les langages shell bash , dash et PowerShell .

La portée dynamique est relativement simple à implémenter. Pour trouver la valeur d'un nom, le programme peut parcourir la pile d'exécution, en vérifiant chaque enregistrement d'activation (le cadre de pile de chaque fonction) pour y trouver une valeur correspondant à ce nom. En pratique, cette opération est optimisée grâce à l'utilisation d'une liste d'associations , qui est une pile de paires nom/valeur. Les paires sont empilées lors de chaque déclaration et dépilées lorsque les variables sortent de leur contexte. La liaison superficielle est une stratégie alternative considérablement plus rapide, utilisant une table de références centrale qui associe chaque nom à sa propre pile de significations. Ceci évite une recherche linéaire lors de l'exécution pour trouver un nom particulier, mais il convient de veiller à la bonne maintenance de cette table. Notez que ces deux stratégies supposent un ordre d'entrée/sortie de type dernier entré, premier sorti ( LIFO ) pour les liaisons de toute variable ; en pratique, toutes les liaisons sont ordonnées de cette manière.

Une implémentation encore plus simple consiste à représenter les variables dynamiques par de simples variables globales. La liaison locale est réalisée en sauvegardant la valeur d'origine dans un emplacement anonyme de la pile, invisible pour le programme. Lorsque la portée de cette liaison se termine, la valeur d'origine est restaurée à partir de cet emplacement. De fait, la portée dynamique a vu le jour de cette manière. Les premières implémentations de Lisp utilisaient cette stratégie évidente pour implémenter les variables locales, et cette pratique perdure dans certains dialectes encore utilisés, tels que GNU Emacs Lisp. La portée lexicale a été introduite ultérieurement dans Lisp. Elle est équivalente au schéma de liaison superficielle décrit précédemment, à ceci près que la table de référence centrale est simplement le contexte de liaison de la variable globale, dans lequel la signification actuelle de la variable correspond à sa valeur globale. La gestion des variables globales est simple. Par exemple, un objet symbole peut disposer d'un emplacement dédié pour sa valeur globale.

La portée dynamique offre une excellente abstraction pour le stockage local au thread , mais son utilisation dans ce contexte ne peut reposer sur la sauvegarde et la restauration d'une variable globale. Une stratégie d'implémentation possible consiste à attribuer à chaque variable une clé locale au thread. Lors de l'accès à la variable, cette clé est utilisée pour accéder à son emplacement mémoire local (grâce au code généré par le compilateur, qui distingue les variables dynamiques des variables lexicales). Si aucune clé locale n'existe pour le thread appelant, l'emplacement global est utilisé. Lorsqu'une variable est liée localement, sa valeur précédente est stockée dans une zone cachée de la pile. Un espace de stockage local est créé sous la clé de la variable, et la nouvelle valeur y est stockée. Les redéfinitions imbriquées de la variable au sein de ce thread sauvegardent et restaurent simplement cet emplacement local. Lorsque le contexte de la redéfinition initiale, la plus externe, se termine, la clé locale est supprimée, exposant à nouveau la version globale de la variable à ce thread.

Avec la transparence référentielle, la portée dynamique est limitée à la pile d'arguments de la fonction courante et coïncide avec la portée lexicale.

Expansion macroéconomique

l'expansion des macros dans un préprocesseur est un exemple clé de portée dynamique de facto. Le langage de macros lui-même transforme uniquement le code source, sans résoudre les noms, mais comme l'expansion est effectuée sur place, lorsque les noms du texte développé sont ensuite résolus (notamment les variables libres), ils le sont en fonction de l'endroit où ils sont développés (en quelque sorte « appelés »), comme si une portée dynamique était en jeu.

Le préprocesseur C , utilisé pour l'expansion des macros , possède de facto une portée dynamique, car il ne résout pas les noms lui-même et est indépendant de l'endroit où la macro est définie. Par exemple, la macro :

une analyse lexicale , développant la macro lors de la tokenisation, mais sans l'analyser syntaxiquement ni résoudre son nom.

Par exemple, dans le code suivant, le nom ade la macro est résolu (après expansion) en la variable locale au niveau du site d'expansion :

C++ et C# , qui servent presque exclusivement à organiser les noms globaux en groupes. D'autres langages proposent des mécanismes, tels que les packages en Ada et les structures en Standard ML , qui combinent cette fonctionnalité avec la possibilité supplémentaire de rendre certains noms visibles uniquement par les membres de leur groupe. Les langages orientés objet permettent souvent aux classes ou aux objets singleton de remplir cette fonction (qu'ils disposent ou non d' un mécanisme dédié). De plus, les langages combinent fréquemment ces approches ; par exemple, les packages de Perl sont très similaires aux espaces de noms de C++, mais peuvent également servir de classes pour la programmation orientée objet ; et Java organise ses variables et fonctions en classes, puis ces classes en packages, à l'instar d'Ada.

Par langue

liaison ou visibilité , notamment pour les variables. Le C est un langage à portée lexicale avec une portée globale (également appelée liaison externe ), une forme de portée de module ou de fichier (appelée liaison interne ) et une portée locale (à l'intérieur d'une fonction). Au sein d'une fonction, les portées peuvent être imbriquées via la portée de bloc. Cependant, le C standard ne prend pas en charge les fonctions imbriquées.

La durée de vie et la visibilité d'une variable sont déterminées par sa classe de stockage . En C, il existe trois types de durée de vie : statique (exécution du programme), automatique (exécution par bloc, allouée sur la pile) et manuelle (allouée sur le tas). Seules les durées de vie statique et automatique sont prises en charge par le compilateur ; la mémoire allouée manuellement doit être gérée manuellement pour chaque variable. En C, il existe trois niveaux de visibilité : la liaison externe (globale), la liaison interne (au niveau du fichier) et la portée de bloc (qui inclut les fonctions). Les portées de bloc peuvent être imbriquées, et différents niveaux de liaison interne sont possibles grâce aux inclusions. La liaison interne en C correspond à la visibilité au niveau de l' unité de traduction , c'est-à-dire au niveau du fichier source après son traitement par le préprocesseur C , incluant notamment toutes les inclusions pertinentes.

Les programmes C sont compilés sous forme de fichiers objets distincts , qui sont ensuite liés en un exécutable ou une bibliothèque par un éditeur de liens . La résolution des noms est donc répartie entre le compilateur, qui résout les noms au sein d'une unité de traduction (ou plus généralement « unité de compilation », bien qu'il s'agisse d'un concept différent), et l'éditeur de liens, qui résout les noms entre les unités de traduction ; voir la section sur l'édition de liens pour plus d'informations.

En C, les variables à portée de bloc entrent dans le contexte lors de leur déclaration (et non en début de bloc), en sortent si une fonction (non imbriquée) est appelée à l'intérieur du bloc, y reviennent lorsque la fonction se termine, et en sortent à nouveau à la fin du bloc. Les variables locales automatiques sont également allouées lors de leur déclaration et libérées à la fin du bloc, tandis que les variables locales statiques sont allouées à l'initialisation du programme et libérées à sa terminaison.

Le programme suivant illustre comment une variable à portée de bloc entre dans le contexte au milieu du bloc, puis sort du contexte (et est en fait désallouée) à la fin du bloc :

" , x );{ printf ( "%c " , x ); char x = 'b' ; printf ( "%c " , x ); }printf ( "%c " , x ); }

Le programme affiche :

permettent une portée lexicale imbriquée.

Rapide

Swift possède une règle similaire à celle de C++ pour les portées, mais contient des modificateurs d'accès différents .

ModificateurPortée immédiateDéposerContenant un module/packageLe reste du monde
ouvrirGo utilise la portée lexicale par blocs.

Java

Java a une portée lexicale.

Une classe Java possède plusieurs types de variables :

Variables locales
Les variables sont définies à l'intérieur d'une méthode ou d'un bloc particulier. Ces variables sont locales à l'endroit où elles sont définies et aux niveaux inférieurs. Par exemple, une boucle à l'intérieur d'une méthode peut utiliser les variables locales de cette méthode, mais l'inverse n'est pas possible. Les variables de la boucle (locales à cette boucle) sont détruites dès que la boucle se termine.
Variables membres
Les champs , également appelés variables , sont des variables déclarées au sein de la classe, en dehors de toute méthode. Par défaut, ces variables sont accessibles à toutes les méthodes de cette classe ainsi qu'à toutes les classes du package.
Paramètres
sont des variables dans les déclarations de méthodes.

En général, un ensemble de crochets définit une portée particulière, mais le comportement des variables de niveau supérieur au sein d'une classe peut varier selon les mots-clés modificateurs utilisés dans leur définition. Le tableau suivant présente l'accès aux membres autorisé par chaque modificateur.

ModificateurClasseEmballerSous-classeMonde
publiqueJavaScript possède des règles de portée simples , mais l'initialisation des variables et les règles de résolution des noms peuvent poser problème. De plus, l'utilisation fréquente des fermetures pour les fonctions de rappel implique que le contexte lexical d'une fonction lors de sa définition (utilisé pour la résolution des noms) peut être très différent de son contexte lexical lors de son appel (sans incidence sur la résolution des noms). Les objets JavaScript bénéficient d'une résolution des noms pour leurs propriétés, mais il s'agit d'un sujet distinct.

JavaScript possède une portée lexicale imbriquée au niveau de la fonction, le contexte global étant le contexte le plus externe. Cette portée est utilisée aussi bien pour les variables que pour les fonctions (c’est-à-dire les déclarations de fonctions, par opposition aux variables de type fonction ) . La portée de bloc, avec les mots-clés ` letand` const, est standard depuis ECMAScript 6. On peut obtenir une portée de bloc en encapsulant le bloc entier dans une fonction, puis en l’exécutant ; c’est ce qu’on appelle le modèle d’expression de fonction immédiatement invoquée (IIFE).

Bien que la portée en JavaScript soit simple (lexicale, au niveau de la fonction), les règles d'initialisation et de résolution de noms associées peuvent prêter à confusion. Premièrement, l'affectation d'une valeur à un nom hors de la portée crée par défaut une nouvelle variable globale, et non locale. Deuxièmement, pour créer une nouvelle variable locale, il faut utiliser le varmot-clé `new`. La variable est alors créée au début de la fonction, avec la valeur ` undefinednull`, et sa valeur lui est affectée lorsque l'expression d'affectation est rencontrée.

Une variable avec un Initialiseur se voit attribuer la valeur de son AssignmentExpression lorsque l' instruction VariableStatement est exécutée, et non lorsque la variable est créée.

Ceci est connu sous le nom de remontée de variables : la déclaration, mais pas l’initialisation, est remontée en haut de la fonction. Troisièmement, accéder à des variables avant leur initialisation produit une exception undefinedplutôt qu’une erreur de syntaxe. Quatrièmement, pour les déclarations de fonctions, la déclaration et l’initialisation sont toutes deux remontées en haut de la fonction, contrairement à l’initialisation des variables. Par exemple, le code suivant produit une boîte de dialogue avec la sortie suivante :indéfini, car la déclaration de la variable locale est remontée, masquant la variable globale, mais l'initialisation ne l'est pas, la variable est donc indéfinie lors de son utilisation :

En JavaScript, les fermetures peuvent être créées à l'aide de fonctions imbriquées, car les fonctions sont des objets de première classe. Le retour d'une fonction imbriquée depuis une fonction englobante inclut les variables locales de cette dernière comme contexte lexical (non local) de la fonction retournée, ce qui produit une fermeture. Par exemple :

Les fermetures sont fréquemment utilisées en JavaScript, notamment pour les fonctions de rappel. En effet, tout appel à une fonction du contexte local comme fonction de rappel ou son retour depuis une fonction crée une fermeture s'il existe des variables non liées dans le corps de la fonction (le contexte de la fermeture étant basé sur les portées imbriquées du contexte lexical courant, ou « chaîne de portée ») ; cela peut être accidentel. Lors de la création d'une fonction de rappel basée sur des paramètres, ces derniers doivent être stockés dans une fermeture, sinon une fermeture référençant les variables du contexte englobant, lesquelles peuvent changer, sera créée par inadvertance.

La résolution des noms des propriétés des objets JavaScript est basée sur l'héritage dans l'arbre prototype — un chemin vers la racine dans l'arbre est appelé chaîne de prototypes — et est distincte de la résolution des noms des variables et des fonctions.

Zézayer

Les dialectes Lisp ont des règles de portée différentes.

Le Lisp original utilisait une portée dynamique ; c'est Scheme , inspiré par ALGOL , qui a introduit la portée statique (lexicale) dans la famille Lisp.

Maclisp utilisait par défaut la portée dynamique dans l'interpréteur et la portée lexicale dans le code compilé, bien que ce dernier puisse accéder aux liaisons dynamiques via des SPECIALdéclarations de variables spécifiques. Cependant, Maclisp traitait la liaison lexicale davantage comme une optimisation que dans les langages modernes, et ne disposait pas de la fonctionnalité de fermeture attendue de la portée lexicale dans les Lisp modernes. Une opération distincte, ` *FUNCTIONfor`, permettait de contourner partiellement ce problème, de manière assez maladroite.

Common Lisp a adopté la portée lexicale de Scheme , tout comme Clojure .

ISLISP possède une portée lexicale pour les variables ordinaires. Il possède également des variables dynamiques, mais celles-ci sont dans tous les cas explicitement marquées ; elles doivent être définies par une defdynamicforme spéciale, liées par une dynamic-letforme spéciale et accessibles par une dynamicforme spéciale explicite.

D'autres dialectes de Lisp, comme Emacs Lisp , utilisent encore la portée dynamique par défaut. Emacs Lisp dispose désormais d'une portée lexicale disponible pour chaque tampon.

Python

En Python, les variables sont définies par trois niveaux de portée : fonction, module et global. Un nom entre dans le contexte au début de sa portée (fonction, module ou global) et en sort lorsqu'une fonction non imbriquée est appelée ou que la portée se termine. L'utilisation d'un nom avant l'initialisation d'une variable provoque une exception d'exécution. Lors d'un simple accès à une variable (sans affectation), la résolution des noms suit la règle LEGB (Local, Englobant, Global, Intégré), qui associe le nom au contexte le plus précis. En revanche, lors d'une affectation, la variable est par défaut déclarée avec une portée commençant au début de son niveau (fonction, module ou global), et non au moment de l'affectation. Ces deux règles peuvent être modifiées par une déclaration `global` globalou `nonlocal` nonlocal(en Python 3) avant utilisation, ce qui permet d'accéder aux variables globales même si une variable non locale les masque, et d'affecter des valeurs à des variables globales ou non locales.

À titre d'exemple simple, une fonction résout une variable dans la portée globale :

None: print(x) x: str = \"global\" f() # prints: global "
def f () -> None : print ( x )x : str = "global" f () # affiche : global

Notez que ` xis` est défini avant f`is`, donc aucune erreur n'est levée, même s'il est défini après sa référence dans la définition de `is` f. Lexicalement, il s'agit d'une référence anticipée , ce qui est autorisé en Python.

Ici, l'affectation crée une nouvelle variable locale, qui ne modifie pas la valeur de la variable globale :

None: x: str = \"f\" print(x) x: str = \"global\" print(x) # prints: global f() # prints: f print(x) # prints: global "
def f () -> None : x : str = "f" print ( x )x : str = "global" print ( x ) # affiche : global f () # affiche : f print ( x ) # affiche : global

L'affectation d'une valeur à une variable à l'intérieur d'une fonction la rend locale à cette fonction ; sa portée est donc limitée à la fonction entière, et toute utilisation de cette variable avant cette affectation provoque une erreur. Cela diffère du C, où la portée d'une variable locale commence à sa déclaration. Le code suivant provoque une erreur :

None: print(x) x: str = \"f\" x: str = \"global\" f() # Traceback (most recent call last): # File \"\", line 1, in # File \"\", line 2, in f # UnboundLocalError: local variable 'x' referenced before assignment "
def f () -> None : print ( x ) x : str = "f"x : str = "global" f () # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # File "<stdin>", line 2, in f # UnboundLocalError: local variable 'x' referenced before assignment

Les règles de résolution de noms par défaut peuvent être modifiées à l'aide des mots-clés `__init__` globalou nonlocal`__reset__` (en Python 3). Dans le code ci-dessous, la global xdéclaration `__init__` dans ` __init__` gsignifie que ` x__init__` est résolu en la variable globale. Celle-ci est donc accessible (puisqu'elle est déjà définie) et l'affectation `__init__` affecte une valeur à la variable globale, plutôt que de déclarer une nouvelle variable locale. Notez qu'aucune globaldéclaration n'est nécessaire dans f`__init__` : puisqu'elle n'affecte aucune valeur à la variable, elle résout par défaut la variable globale.

None: print(x) def g() -> None: global x print(x) x = \"g\" x: str = \"global\" f() # prints: global g() # prints: global f() # prints: g "
def f () -> None : print ( x )def g () -> None : global x print ( x ) x = "g"x : str = "global" f () # affiche : global g () # affiche : global f () # affiche : g

globalpeut également être utilisé pour les fonctions imbriquées. Outre la possibilité d'affecter une valeur à une variable globale, comme dans une fonction non imbriquée, cela permet aussi d'accéder à la variable globale même en présence d'une variable non locale :

None: def g() -> None: global x print(x) x: str = \"f\" g() x: str = \"global\" f() # prints: global "
def f () -> None : def g () -> None : global x print ( x ) x : str = "f" g ()x : str = "global" f () # affiche : global

Pour les fonctions imbriquées, il existe également une nonlocaldéclaration, pour l'affectation à une variable non locale, similaire à celle utilisée globaldans une fonction non imbriquée :

None: def g() -> None: nonlocal x # Python 3 only x = \"g\" x: str = \"f\" g() print(x) x: str = \"global\" f() # prints: g print(x) # prints: global "
def f () -> None : def g () -> None : nonlocal x # Python 3 uniquement x = "g" x : str = "f" g () print ( x )x : str = "global" f () # affiche : g print ( x ) # affiche : global

R

R est un langage à portée lexicale, contrairement à d'autres implémentations de S où les valeurs des variables libres sont déterminées par un ensemble de variables globales, tandis qu'en R, elles sont déterminées par le contexte dans lequel la fonction a été créée. Les contextes de portée sont accessibles grâce à diverses fonctionnalités (telles que parent.frame()) qui peuvent simuler l'expérience d'une portée dynamique si le programmeur le souhaite.

Il n'y a pas de portée de bloc :

Plus d articles de Worldlex Wiki

Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

Explorer l index