<<-
est le plus utile en conjonction avec des fermetures pour maintenir l'état. Voici une section d'un de mes articles récents:
Une fermeture est une fonction écrite par une autre fonction. Les fermetures sont ainsi appelées car elles englobent l'environnement de la fonction parent et peuvent accéder à toutes les variables et paramètres de cette 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. L'autre niveau (l'enfant) fait le travail. L'exemple suivant montre comment utiliser cette idée pour générer une famille de fonctions de puissance. La fonction parent ( power
) crée des fonctions enfants ( square
et cube
) qui font réellement le travail 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 capacité de gérer des variables à deux niveaux permet également de maintenir l'état à travers les appels de fonction en permettant à une fonction de modifier des 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 de flèche unique habituelle ( <-
) qui fonctionne toujours au niveau actuel, l'opérateur de double flèche peut modifier les 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 new_counter
exécution, 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 l'environnement englobant. Lorsque les fermetures counter_one
et counter_two
sont exécutées, chacun modifie le compteur dans son environnement englobant puis renvoie le décompte actuel.
counter_one <- new_counter()
counter_two <- new_counter()
counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
Cela aide à penser
<<-
comme équivalent àassign
(si vous définissez leinherits
paramètre de cette fonction surTRUE
). L'avantageassign
est qu'il vous permet de spécifier plus de paramètres (par exemple l'environnement), donc je préfère utiliserassign
over<<-
dans la plupart des cas.Utiliser
<<-
etassign(x, value, inherits=TRUE)
signifie que «les environnements englobants 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 avec ce nom, et il l'attribuera à cela. Cela peut être 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 j'exécute une grande simulation 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 ou de la
apply
boucle donnée . C'est très utile, surtout si vous avez des inquiétudes au sujet d'une grande boucle se terminant de manière inattendue (par exemple, une déconnexion de base de données), auquel cas vous pourriez tout perdre dans le processus. Cela équivaudrait à écrire vos résultats dans une base de données ou un fichier pendant un long processus, sauf qu'il stocke les résultats dans l'environnement R.Mon principal avertissement avec ceci: soyez prudent car vous travaillez maintenant avec des variables globales, en particulier lors de l'utilisation
<<-
. Cela signifie que vous pouvez vous retrouver avec des situations dans lesquelles une fonction utilise une valeur d'objet de l'environnement, alors que vous vous attendiez à ce qu'elle en utilise une qui a été fournie en tant que paramètre. C'est l'une des principales choses que la programmation fonctionnelle essaie d'éviter (voir effets secondaires ). J'évite ce problème en attribuant mes valeurs à des noms de variables uniques (en utilisant coller avec un ensemble ou des paramètres uniques) qui ne sont jamais utilisés dans la fonction, mais juste utilisés pour la mise en cache et au cas où je devrais récupérer plus tard (ou faire des méta -analyse sur les résultats intermédiaires).la source
Un endroit où j'ai utilisé
<<-
était dans les interfaces graphiques simples utilisant tcl / tk. Certains des premiers exemples l'ont - car vous devez faire une distinction entre les variables locales et globales pour l'état complet. Voir par exemplequi utilise
<<-
. Sinon, je suis d'accord avec Marek :) - une recherche Google peut aider.la source
tkdensity
en quelque sorte trouver dans R 3.6.0.la source
<<-
. Une boucle for serait plus claire dans ce cas.À ce sujet, j'aimerais souligner que l'
<<-
opérateur se comportera de manière étrange lorsqu'il est appliqué (de manière incorrecte) dans une boucle for (il peut y avoir d'autres cas également). Compte tenu du code suivant:vous pourriez vous attendre à ce que la fonction retourne la somme attendue, 6, mais à la place, elle renvoie 0, avec une variable globale
mySum
créée et affectée à la valeur 3. Je ne peux pas expliquer complètement ce qui se passe ici mais certainement le corps d'un for loop n'est pas un nouveau «niveau» de portée. Au lieu de cela, il semble que R regarde en dehors de lafortest
fonction, ne trouve pas demySum
variable à affecter, crée donc une et attribue la valeur 1, la première fois dans la boucle. Lors des itérations suivantes, le RHS dans l'affectation doit faire référence à lamySum
variable interne (inchangée) tandis que le LHS se réfère à la variable globale. Par conséquent, chaque itération remplace la valeur de la variable globale par la valeur de cette itérationi
, d'où la valeur 3 à la sortie de la fonction.J'espère que cela aide quelqu'un - cela m'a déconcerté pendant quelques heures aujourd'hui! (BTW, remplacez simplement
<<-
par<-
et la fonction fonctionne comme prévu).la source
mySum
n'est jamais incrémenté mais uniquement le globalmySum
. Par conséquent, à chaque itération de la boucle for, le globalmySum
obtient la valeur0 + i
. Vous pouvez suivre cela avecdebug(fortest)
.<-
partout de manière cohérente dans la fonction si vous souhaitez uniquement mettre à jour la variable locale à l'intérieur de la fonction.L'
<<-
opérateur peut également être utile pour les classes de référence lors de l'écriture de méthodes de référence . Par exemple:la source