181 votes

Comment utiliser "<<-" (affectation de portée) dans R ?

Je viens de lire un article sur scoping dans l'intro R et je suis très curieux d'en savoir plus sur la <<- affectation.

Le manuel présente un exemple (très intéressant) de <<- J'ai l'impression d'avoir compris. Ce qui me manque encore, c'est le contexte dans lequel cela peut être utile.

J'aimerais donc que vous me donniez des exemples (ou des liens vers des exemples) sur les cas où l'utilisation de <<- peut être intéressante/utile. Quels sont les risques liés à son utilisation (il semble facile d'en perdre la trace), et quels sont les conseils que vous souhaiteriez partager.

237voto

hadley Points 33766

<<- est plus utile en conjonction avec les fermetures pour maintenir l'état. Voici un extrait d'un de mes récents articles :

Une fermeture est une fonction écrite par une autre fonction. Les fermetures sont appelées ainsi parce qu'elles enfermer l'environnement du parent et peut accéder à toutes les variables et à tous les paramètres de cette fonction fonction. Ceci est utile car cela nous permet d'avoir deux niveaux de paramètres. Un niveau de paramètres (le parent) contrôle le fonctionnement de la fonction. fonctionne. L'autre niveau (l'enfant) effectue le travail. L'exemple exemple suivant montre comment utiliser cette idée pour générer une famille de fonctions de puissance. La fonction mère ( power ) crée des fonctions enfants ( square y cube ) qui effectuent le travail le plus difficile.

power <- function(exponent) {
  function(x) x ^ exponent
}

square <- power(2)
square(2) # -> [1] 4
square(4) # -> [1] 16

cube <- power(3)
cube(2) # -> [1] 8
cube(4) # -> [1] 64

La possibilité de gérer les variables à deux niveaux permet également de maintenir l'état entre les invocations de fonctions en autorisant une fonction à modifier les variables dans l'environnement de son parent. La clé de la gestion des variables à différents niveaux est l'opérateur d'affectation à double flèche <<- . Contrairement à l'affectation habituelle d'une seule flèche ( <- ) qui agit toujours sur le niveau courant, l'opérateur double flèche peut modifier des variables dans les niveaux parents.

Cela permet de gérer un compteur qui enregistre le nombre de fois qu'une fonction a été appelée, comme le montre l'exemple suivant. À chaque fois que la fonction new_counter est exécuté, il crée un environnement, initialise le compteur i dans cet environnement, puis crée une nouvelle fonction.

new_counter <- function() {
  i <- 0
  function() {
    # do something useful, then ...
    i <<- i + 1
    i
  }
}

La nouvelle fonction est une fermeture et son environnement est le milieu environnant. Lorsque les fermetures counter_one y counter_two sont exécutés, chacun d'eux modifie le compteur dans l'environnement qui l'entoure, puis renvoie le compteur actuel.

counter_one <- new_counter()
counter_two <- new_counter()

counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1

56voto

Shane Points 40885

Il est utile de penser à <<- comme équivalent à assign (si vous avez défini l'option inherits dans cette fonction à TRUE ). L'avantage de la assign est qu'il permet de spécifier plus de paramètres (par exemple, l'environnement), et je préfère donc utiliser assign plus <<- dans la plupart des cas.

Utilisation <<- y assign(x, value, inherits=TRUE) signifie que "les environnements environnants de l'environnement fourni sont recherchés jusqu'à ce que la variable 'x' soit rencontrée". En d'autres termes, il continuera à parcourir les environnements dans l'ordre jusqu'à ce qu'il trouve une variable portant ce nom, et il l'affectera à cette variable. Cela peut se faire dans le cadre d'une fonction ou dans l'environnement global.

Afin de comprendre ce que font ces fonctions, vous devez également comprendre les environnements R (par exemple en utilisant search ).

J'utilise régulièrement ces fonctions lorsque je réalise une simulation de grande ampleur et que je souhaite enregistrer des résultats intermédiaires. Cela vous permet de créer l'objet en dehors de la portée de la fonction donnée ou de la fonction apply boucle. C'est très utile, surtout si vous craignez qu'une grande boucle ne se termine de manière inattendue (par exemple, une déconnexion de la base de données), auquel cas vous pourriez tout perdre au cours du processus. Cela équivaudrait à écrire vos résultats dans une base de données ou un fichier au cours d'un processus de longue durée, sauf qu'il s'agit de stocker les résultats dans l'environnement R.

Mon principal avertissement est le suivant : soyez prudent car vous travaillez maintenant avec des variables globales, en particulier lors de l'utilisation de <<- . Cela signifie que vous pouvez vous retrouver dans des situations où une fonction utilise une valeur d'objet provenant de l'environnement, alors que vous vous attendiez à ce qu'elle utilise une valeur fournie en tant que paramètre. C'est l'une des principales choses que la programmation fonctionnelle tente d'éviter (voir les effets secondaires ). J'évite ce problème en assignant mes valeurs à des noms de variables uniques (en utilisant la fonction paste avec un ensemble de paramètres uniques) qui ne sont jamais utilisées dans la fonction, mais seulement pour la mise en cache et au cas où j'aurais besoin de les récupérer plus tard (ou de faire une méta-analyse sur les résultats intermédiaires).

10voto

Dirk Eddelbuettel Points 134700

Un endroit où j'ai utilisé <<- était dans les interfaces graphiques simples utilisant tcl/tk. Certains des premiers exemples l'ont -- car il faut faire une distinction entre les variables locales et globales pour l'état. Voir par exemple

 library(tcltk)
 demo(tkdensity)

qui utilise <<- . Sinon, je suis d'accord avec Marek :) -- une recherche sur Google peut être utile.

7voto

Matthew Wise Points 221

À ce sujet, je tiens à souligner que la <<- se comporte de manière étrange lorsqu'il est appliqué (de manière incorrecte) dans une boucle for (il peut y avoir d'autres cas). Voici le code suivant :

fortest <- function() {
    mySum <- 0
    for (i in c(1, 2, 3)) {
        mySum <<- mySum + i
    }
    mySum
}

on pourrait s'attendre à ce que la fonction renvoie la somme attendue, 6, mais au lieu de cela elle renvoie 0, avec une variable globale mySum qui est créée et à laquelle est attribuée la valeur 3. Je ne peux pas expliquer complètement ce qui se passe ici, mais il est certain que le corps d'une boucle for est pas un nouveau champ d'application "niveau". Au lieu de cela, il semble que R regarde à l'extérieur de la portée fortest ne trouve pas de fonction mySum Il en crée donc une et lui attribue la valeur 1, la première fois que la boucle est parcourue. Lors des itérations suivantes, le RHS de l'affectation doit faire référence à la variable interne (inchangée). mySum tandis que la variable LHS fait référence à la variable globale. Par conséquent, chaque itération écrase la valeur de la variable globale en la remplaçant par la valeur de i Il a donc la valeur 3 à la sortie de la fonction.

J'espère que cela aidera quelqu'un - j'ai été bloqué pendant deux heures aujourd'hui ! (BTW, il suffit de remplacer <<- avec <- et la fonction fonctionne comme prévu).

5voto

lcgong Points 74
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X