Quelles sont les différences entre les opérateurs d'assignation =
et <-
dans R?
Je sais que les opérateurs sont légèrement différents, comme le montre cet exemple
x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"
Mais est-ce la seule différence?
La différence entre opérateurs d'assignation est plus claire lorsque vous les utilisez pour définir une valeur d'argument dans un appel de fonction. Par exemple:
_median(x = 1:10)
x
## Error: object 'x' not found
_
Dans ce cas, x
est déclaré dans l'étendue de la fonction et n'existe donc pas dans l'espace de travail de l'utilisateur.
_median(x <- 1:10)
x
## [1] 1 2 3 4 5 6 7 8 9 10
_
Dans ce cas, x
est déclaré dans l'espace de travail de l'utilisateur. Vous pouvez donc l'utiliser après la fin de l'appel de la fonction.
La communauté R préfère généralement utiliser _<-
_ pour l'affectation (autre que dans les signatures de fonction) pour assurer la compatibilité avec les versions (très) anciennes de S-Plus. Notez que les espaces aident à clarifier des situations telles que
_x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3
_
La plupart des IDE R ont des raccourcis clavier pour rendre _<-
_ plus facile à taper. Ctrl + = en architecte, Alt + - dans RStudio (Option + - sous macOS), Shift + - (trait de soulignement) dans emacs + ESS.
Si vous préférez écrire _=
_ à _<-
_ mais souhaitez utiliser le symbole d'affectation le plus courant pour le code rendu public (sur CRAN, par exemple), vous pouvez utiliser l'un des tidy_*
fonctionne dans le package formatR
pour remplacer automatiquement _=
_ par _<-
_.
_library(formatR)
tidy_source(text = "x=1:5", arrow = TRUE)
## x <- 1:5
_
La réponse à la question "Pourquoi _x <- y = 5
_ génère-t-il une erreur mais pas _x <- y <- 5
_?" est "C'est la magie contenue dans l'analyseur". La syntaxe de R contient beaucoup de cas ambigus qui doivent être résolus d'une manière ou d'une autre. L'analyseur choisit de résoudre les bits de l'expression dans des ordres différents selon que _=
_ ou _<-
_ a été utilisé.
Pour comprendre ce qui se passe, vous devez savoir que l'affectation renvoie en mode silencieux la valeur attribuée. Vous pouvez voir cela plus clairement en imprimant explicitement, par exemple print(x <- 2 + 3)
.
Deuxièmement, il est plus clair si nous utilisons la notation de préfixe pour l’affectation. Alors
_x <- 5
`<-`(x, 5) #same thing
y = 5
`=`(y, 5) #also the same thing
_
L'analyseur interprète _x <- y <- 5
_ comme
_`<-`(x, `<-`(y, 5))
_
On pourrait s’attendre à ce que _x <- y = 5
_ soit alors
_`<-`(x, `=`(y, 5))
_
mais en réalité, il est interprété comme
_`=`(`<-`(x, y), 5)
_
En effet, _=
_ a une priorité inférieure à _<-
_, comme indiqué sur la page d'aide ?Syntax
.
Le guide de style R de Google simplifie le problème en interdisant le "=" pour l'attribution. Pas un mauvais choix.
https://google.github.io/styleguide/Rguide.xml
Le manuel R explique en détail Nice sur les 5 opérateurs d’affectation.
http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html
x = y = 5
est équivalent à x = (y = 5)
, car les opérateurs d'assignation "groupent" de droite à gauche, ce qui fonctionne. Signification: attribuer 5 à y
, en laissant le nombre 5; et ensuite assigner ce 5 à x
.
Ce n'est pas la même chose que (x = y) = 5
, qui ne marche pas! Signification: attribuer la valeur de y
à x
, en laissant la valeur de y
; et ensuite assigner 5 à, euh ..., quoi exactement?
Lorsque vous mélangez les différents types d'opérateurs d'assignation, <-
se lie plus étroitement que =
. Donc, x = y <- 5
est interprété comme x = (y <- 5)
, ce qui est logique.
Malheureusement, x <- y = 5
est interprété comme (x <- y) = 5
, ce qui est le cas, cela ne fonctionne pas!
Voir ?Syntax
et ?assignOps
pour les règles de priorité (liaison) et de regroupement.
Selon John Chambers, l'opérateur =
n'est autorisé qu'au "niveau supérieur", ce qui signifie qu'il n'est pas autorisé dans les structures de contrôle telles que if
, ce qui rend illégale l'erreur de programmation suivante.
> if(x = 0) 1 else x
Error: syntax error
Comme il l'écrit, "le fait de ne pas autoriser la nouvelle forme d'assignation [=] dans les expressions de contrôle évite les erreurs de programmation (telles que l'exemple ci-dessus) qui sont plus probables avec l'opérateur égal que avec d'autres affectations S."
Vous pouvez y parvenir s'il est "isolé de la structure logique environnante, par des accolades ou une paire de parenthèses supplémentaire", de sorte que if ((x = 0)) 1 else x
puisse fonctionner.
Quelles sont les différences entre les opérateurs d'assignation
=
et<-
dans R?
Comme votre exemple le montre, =
et <-
ont une priorité légèrement différente sur les opérateurs (ce qui détermine l'ordre d'évaluation lorsqu'ils sont mélangés dans la même expression). En fait, ?Syntax
in R donne le tableau de priorité des opérateurs suivant, du plus élevé au plus bas:
… ‘-> ->>’ rightwards assignment ‘<- <<-’ assignment (right to left) ‘=’ assignment (right to left) …
Mais est-ce la seule différence?
Puisque vous vous interrogiez sur les opérateurs d'assignation : oui, c'est la seule différence. Cependant, vous seriez pardonné de croire le contraire. Même la documentation R de ?assignOps
affirme qu'il y a plus de différences:
L'opérateur
<-
peut être utilisé n'importe où, alors que l'opérateur=
est uniquement autorisé au niveau supérieur (par exemple, dans l'expression complète tapée dans la commande Invite) ou en tant que sous-expression dans une liste d'expressions encadrée.
Ne mettons pas un point trop fin dessus: la documentation R est (subtilement) fausse [ 1 ]. C’est facile à montrer: nous devons simplement trouver un contre-exemple de l’opérateur =
qui n’est pas (a) au niveau supérieur, ni (b) une sous-expression dans une liste d’expressions encadrée (par exemple, {…; …}
). - Sans plus tarder:
x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1
Clairement, nous avons effectué une tâche en utilisant =
, en dehors des contextes (a) et (b). Alors, pourquoi la documentation d'une fonctionnalité de base du langage R est-elle erronée depuis des décennies?
C’est parce que, dans la syntaxe de R, le symbole =
a deux significations distinctes qui sont systématiquement combinées:
=
, il n'effectue aucune action au moment de l'exécution, il modifie simplement la façon dont une expression est analysée.Voyons voir.
Dans n'importe quel morceau de code de la forme générale…
‹function_name›(‹argname›=‹value›, …)
‹function_name›(‹args›, ‹argname›=‹value›, …)
… Le =
est le jeton qui définit le passage d'argument nommé: il s'agit et non de l'opérateur d'affectation. De plus, =
est totalement interdit dans certains contextes syntaxiques:
if (‹var›=‹value›) …
while (‹var›=‹value›) …
for (‹var›=‹value› in ‹value2›) …
for (‹var1› in ‹var2›=‹value›) …
N'importe lequel de ceux-ci générera une erreur “inattendu '=' in‹ bla ›”.
Dans tout autre contexte, =
fait référence à l'appel de l'opérateur d'affectation. En particulier, le simple fait de mettre des parenthèses autour de la sous-expression rend l’un des éléments ci-dessus (a) valide et (b) une affectation . Par exemple, les tâches suivantes sont effectuées:
median((x = 1 : 10))
Mais aussi:
if (! (nf = length(from))) return()
Vous pouvez maintenant objecter qu'un tel code est atroce (et vous avez peut-être raison). Mais j’ai pris ce code dans la fonction base::file.copy
(en remplaçant <-
par =
) - c’est un motif omniprésent dans la majeure partie de la base de code R principale.
Le explication originale de John Chambers , sur laquelle la documentation R est probablement basée, explique en fait ceci correctement:
L'attribution de [
=
est] autorisée à deux endroits seulement de la grammaire: au niveau supérieur (en tant que programme complet ou expression typée par l'utilisateur); et lorsqu'ils sont isolés de la structure logique environnante, par des accolades ou une paire supplémentaire de parenthèses.
Une confession: j'ai menti plus tôt. Là , il y a une différence supplémentaire entre les opérateurs =
et <-
: ils appellent des fonctions distinctes. Par défaut, ces fonctions font la même chose, mais vous pouvez les remplacer séparément pour modifier le comportement. En revanche, <-
et ->
(affectation de gauche à droite), bien que syntaxiquement distincts, appellent toujours la même fonction . Remplacer l'un remplace l'autre. Sachant que cela est rarement pratique mais cela peut être utilisé pour quelques manigances amusantes .
Les opérateurs <-
et =
sont affectés dans l'environnement dans lequel ils sont évalués. L'opérateur <-
peut être utilisé n'importe où, alors que l'opérateur =
n'est autorisé qu'au niveau supérieur (par exemple, dans l'expression complète tapée à l'invite de commande) ou sous l'une des sous-expressions d'une liste d'expressions encadrée.
Cela peut également aider à mieux comprendre la différence entre ces deux opérateurs:
df <- data.frame(
a = rnorm(10),
b <- rnorm(10)
)
Pour le premier élément, R a attribué des valeurs et un nom propre, tandis que le nom du deuxième élément est un peu étrange.
str(df)
# 'data.frame': 10 obs. of 2 variables:
# $ a : num 0.6393 1.125 -1.2514 0.0729 -1.3292 ...
# $ b....rnorm.10.: num 0.2485 0.0391 -1.6532 -0.3366 1.1951 ...
R version 3.3.2 (2016-10-31); macOS Sierra 10.12.1