¿Cuáles son las diferencias entre los operadores de asignación “=” y “<-” en R?

712

¿Cuáles son las diferencias entre los operadores de asignación =y <-en R?

Sé que los operadores son ligeramente diferentes, como muestra este ejemplo

x <- y <- 5
x = y = 5
x = y <- 5
x <- y = 5
# Error in (x <- y) = 5 : could not find function "<-<-"

¿Pero es esta la única diferencia?

csgillespie
fuente
45
Como se señaló aquí, los orígenes del <-símbolo provienen de viejos teclados APL que en realidad tenían una sola <-tecla.
joran

Respuestas:

97

¿Cuáles son las diferencias entre los operadores de asignación =y <-en R?

Como muestra su ejemplo, =y <-tienen una precedencia de operador ligeramente diferente (que determina el orden de evaluación cuando se mezclan en la misma expresión). De hecho, ?Syntaxen R da la siguiente tabla de precedencia de operadores, de mayor a menor:

-> ->>’           rightwards assignment<- <<-’           assignment (right to left)=’                assignment (right to left)

¿Pero es esta la única diferencia?

Como estaba preguntando acerca de los operadores de asignación : sí, esa es la única diferencia. Sin embargo, sería perdonado por creer lo contrario. Incluso la documentación R de ?assignOpsafirma que hay más diferencias:

El operador <-se puede usar en cualquier lugar, mientras que el operador =solo se permite en el nivel superior (por ejemplo, en la expresión completa escrita en el símbolo del sistema) o como una de las subexpresiones en una lista de expresiones entre paréntesis.

No pongámosle un punto demasiado fino: la documentación de R es (sutilmente) incorrecta [ 1 ] . Esto es fácil de mostrar: solo necesitamos encontrar un contraejemplo del =operador que no esté (a) en el nivel superior, ni (b) una subexpresión en una lista de expresiones entre paréntesis (es decir {…; …}). - Sin más preámbulos:

x
# Error: object 'x' not found
sum((x = 1), 2)
# [1] 3
x
# [1] 1

Claramente hemos realizado una tarea, utilizando =, fuera de los contextos (a) y (b). Entonces, ¿por qué la documentación de una característica principal del lenguaje R ha estado mal durante décadas?

Es porque en la sintaxis de R el símbolo =tiene dos significados distintos que se combinan habitualmente:

  1. El primer significado es como operador de asignación . Esto es todo de lo que hemos hablado hasta ahora.
  2. El segundo significado no es un operador, sino más bien un token de sintaxis que señala un argumento con nombre que pasa en una llamada de función. A diferencia del =operador, no realiza ninguna acción en tiempo de ejecución, simplemente cambia la forma en que se analiza una expresión.

Veamos.

En cualquier código de la forma general ...

‹function_name›(‹argname› = ‹value›,)
‹function_name›(‹args›, ‹argname› = ‹value›,)

... el =es el símbolo que define el llamado argumento que pasa: es no el operador de asignación. Además, =está completamente prohibido en algunos contextos sintácticos:

if (‹var› = ‹value›) …
while (‹var› = ‹value›) …
for (‹var› = ‹value› in ‹value2›) …
for (‹var1› in ‹var2› = ‹value›) …

Cualquiera de estos generará un error “inesperado '=' en ‹bla›”.

En cualquier otro contexto, se =refiere a la llamada del operador de asignación. En particular, simplemente poner paréntesis alrededor de la subexpresión hace que cualquiera de los anteriores (a) sea válido y (b) una asignación . Por ejemplo, lo siguiente realiza la asignación:

median((x = 1 : 10))

Pero también:

if (! (nf = length(from))) return()

Ahora puede objetar que dicho código es atroz (y puede que tenga razón). Pero tomé este código de la base::file.copyfunción (reemplazando <-con =): es un patrón generalizado en gran parte de la base de código R principal.

La explicación original de John Chambers , en la que probablemente se basa la documentación de R, en realidad explica esto correctamente:

[la =asignación está] permitida en solo dos lugares en la gramática: en el nivel superior (como un programa completo o una expresión escrita por el usuario); y cuando se aísla de la estructura lógica circundante, mediante llaves o un par adicional de paréntesis.


Una confesión: mentí antes. No es una diferencia adicional entre el =y <-los operadores: se llama a funciones distintas. Por defecto, estas funciones hacen lo mismo, pero puede anular cualquiera de ellas por separado para cambiar el comportamiento. Por el contrario, <-y ->(asignación de izquierda a derecha), aunque sintácticamente distintas, siempre llaman a la misma función. Anular uno también anula al otro. Saber esto rara vez es práctico, pero puede usarse para algunas travesuras divertidas .

Konrad Rudolph
fuente
1
Acerca de la precedencia y los errores en el documento de R, la precedencia de ?está realmente en el medio =y <-, lo que tiene consecuencias importantes al anular ? , y prácticamente nada de lo contrario.
Moody_Mudskipper
¡@Moody_Mudskipper es extraño! Parece que tienes razón, pero de acuerdo con el código fuente ( main/gram.y), la precedencia de ?está correctamente documentada y es menor que ambas =y <-.
Konrad Rudolph
No hablo C, pero supongo que =recibirá un tratamiento especial antes de construir el árbol de análisis. Quizás relacionado con argumentos de función, tiene sentido que foo(x = a ? b)lo busquemos =antes de analizar el resto de la expresión.
Moody_Mudskipper
@Moody_Mudskipper le pregunté a r-devel
Konrad Rudolph
2
@Moody_Mudskipper FWIW esto finalmente se soluciona en 4.0.0.
Konrad Rudolph
661

La diferencia en los operadores de asignación es más clara cuando los usa para establecer un valor de argumento en una llamada de función. Por ejemplo:

median(x = 1:10)
x   
## Error: object 'x' not found

En este caso, xse declara dentro del alcance de la función, por lo que no existe en el espacio de trabajo del usuario.

median(x <- 1:10)
x    
## [1]  1  2  3  4  5  6  7  8  9 10

En este caso, xse declara en el espacio de trabajo del usuario, por lo que puede usarlo después de que se haya completado la llamada a la función.


Existe una preferencia general entre la comunidad R por usar <-para la asignación (que no sea en firmas de funciones) para la compatibilidad con versiones (muy) antiguas de S-Plus. Tenga en cuenta que los espacios ayudan a aclarar situaciones como

x<-3
# Does this mean assignment?
x <- 3
# Or less than?
x < -3

La mayoría de los IDE de R tienen métodos abreviados de teclado para <-facilitar la escritura. Ctrl+ =en Architect, Alt+ -en RStudio ( Option+ -bajo macOS), Shift+ -(subrayado) en emacs + ESS.


Si prefiere escribir =a <-pero desea utilizar el símbolo de asignación más común para el código publicado públicamente (en CRAN, por ejemplo), entonces se puede utilizar una de las tidy_*funciones en el formatRpaquete para reemplazar automáticamente =con <-.

library(formatR)
tidy_source(text = "x=1:5", arrow = TRUE)
## x <- 1:5

La respuesta a la pregunta "¿Por qué x <- y = 5arroja un error pero no x <- y <- 5?" es "Todo depende de la magia contenida en el analizador". La sintaxis de R contiene muchos casos ambiguos que deben resolverse de una forma u otra. El analizador elige resolver los bits de la expresión en diferentes órdenes dependiendo de si se utilizó =o no <-.

Para comprender lo que está sucediendo, debe saber que la asignación devuelve silenciosamente el valor asignado. Puede verlo más claramente imprimiendo explícitamente, por ejemplo print(x <- 2 + 3).

En segundo lugar, es más claro si usamos la notación de prefijo para la asignación. Entonces

x <- 5
`<-`(x, 5)  #same thing

y = 5
`=`(y, 5)   #also the same thing

El analizador interpreta x <- y <- 5como

`<-`(x, `<-`(y, 5))

Podríamos esperar que x <- y = 5entonces sería

`<-`(x, `=`(y, 5))

pero en realidad se interpreta como

`=`(`<-`(x, y), 5)

Esto se debe a que =tiene una precedencia menor que <-, como se muestra en la ?Syntaxpágina de ayuda.

Algodón Richie
fuente
44
Esto también se menciona en el capítulo 8.2.26 de The R Inferno por Patrick Burns (No soy yo, pero una recomendación de todos modos)
Uwe
3
Sin embargo, median((x = 1:10))tiene el mismo efecto que median(x <- 1:10).
Francesco Napolitano
2
Realmente no los considero atajos, en cualquier caso presionas la misma cantidad de teclas
yosemite_k
55
Me acabo de dar cuenta de que su explicación de cómo x <- x = 5se interpreta es un poco errónea: en realidad, R la interpreta como ​`<-<-`(x, y = 5, value = 5)(que en sí misma es más o menos equivalente a tmp <- x; x <- `<-<-`(tmp, y = 5, value = 5)). ¡Ay!
Konrad Rudolph el
44
... Y acabo de darme cuenta de que la primera parte de esta respuesta es incorrecta y, desafortunadamente, es bastante engañosa porque perpetúa un error común: la forma en que se usa =en una llamada de función no realiza la asignación , y no es un operador de asignación. Es una expresión R analizada completamente distinta, que simplemente usa el mismo carácter. Además, el código que muestra no "declara" xen el ámbito de la función. La declaración de función realiza dicha declaración. La llamada a la función no (se vuelve un poco más complicada con ...argumentos con nombre ).
Konrad Rudolph el
103

La guía de estilo R de Google simplifica el problema al prohibir el "=" para la asignación. No es una mala elección.

https://google.github.io/styleguide/Rguide.xml

El manual de R entra en detalles agradables en los 5 operadores de asignación.

http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html

Nosredna
fuente
133
La desventaja de la asignación accidental x<-ycuando x < -yse quiso decir, me molesta tanto que personalmente prefiero =. Hacer que su código dependa del espacio en blanco presente no me parece bueno. ¿Está bien sugerir espacios como consejos de estilo pero para que su código se ejecute de manera diferente si hay un espacio o no? ¿Qué sucede si reformatea su código, o usa buscar y reemplazar, el espacio en blanco a veces puede desaparecer y el código sale mal? Eso no es un problema con =. IIUC, prohibir =equivale a exigir " <- "; es decir, 3 caracteres incluyendo un espacio, no solo " <-".
Matt Dowle
12
Tenga en cuenta que TRUER. no considera ningún valor distinto de 0. Por lo tanto, si tiene la intención de probar si xes menor que -y, puede escribir if (x<-y)lo que no advertirá ni producirá errores, y parecerá que funciona bien. Sin embargo, solo será FALSEcuando y=0.
Matt Dowle
44
Si prohíbe =y usa <- , es difícil argumentar que grep "[^<]<-[^ ]" *.Rno se necesita un paso adicional . =no necesita tal grep.
Matt Dowle
34
¿Por qué lastimar tus ojos y dedos <-si puedes usar =? En el 99,99% de las veces =está bien. Sin <<-embargo, a veces es necesario , que es una historia diferente.
Fernando
10
El enfoque en <- es quizás una de las razones poco convincentes para la falta de + = y - =.
Chris
37

x = y = 5es equivalente a x = (y = 5), porque los operadores de asignación "agrupan" de derecha a izquierda, lo que funciona. Significado: asignar 5 a y, dejando el número 5; y luego asigna ese 5 a x.

¡Esto no es lo mismo (x = y) = 5que no funciona! Significado: asignar el valor de ya x, dejando el valor de y; y luego asigna 5 a, umm ..., ¿qué exactamente?

Cuando combina los diferentes tipos de operadores de asignación, los <-enlaces se ajustan más que =. Entonces x = y <- 5se interpreta como x = (y <- 5), que es el caso que tiene sentido.

Lamentablemente, x <- y = 5se interpreta como (x <- y) = 5, que es el caso que no funciona!

Vea ?Syntaxy ?assignOpspara la precedencia (enlace) y las reglas de agrupación.

Steve Pitchers
fuente
Sí, como dijo la respuesta de Konrad Rudolph que <- <<-está arriba de la = tabla de precedencia, lo que significa <-que se ejecutará primero. Por lo tanto, x <- y = 5debe ejecutarse como (x <- y) = 5.
Nick Dong
1
@ Nick Dong Sí, de hecho. Con ayuda, la tabla de precedencia del operador se documenta inequívocamente en ? Syntax {base} .
Steve Pitchers
33

Según John Chambers, el operador =solo está permitido en "el nivel superior", lo que significa que no está permitido en estructuras de control como if, lo que hace que el siguiente error de programación sea ilegal.

> if(x = 0) 1 else x
Error: syntax error

Como escribe, "No permitir la nueva forma de asignación [=] en las expresiones de control evita errores de programación (como el ejemplo anterior) que son más probables con el operador igual que con otras asignaciones de S".

Puede lograr hacer esto si está "aislado de la estructura lógica circundante, mediante llaves o un par de paréntesis extra", así if ((x = 0)) 1 else xfuncionaría.

Ver http://developer.r-project.org/equalAssign.html

Aaron dejó Stack Overflow
fuente
11
Es un error común, x==0casi siempre se entiende en su lugar.
Aaron dejó Stack Overflow el
14
Ah, sí, pasé por alto que dijiste "error de programación". En realidad, son buenas noticias que esto causa un error. ¡Y una buena razón para preferir x=0que la tarea termine x<-0!
Steve Pitchers
77
Sí, es bueno que esto cause un error, aunque extraigo una lección diferente sobre qué preferir; Elijo usar la =menor cantidad posible porque =y me ==veo muy similar.
Aaron dejó Stack Overflow el
2
La forma en que se presenta este ejemplo es muy extraña para mí. if(x = 0) 1 else xarroja un error, ayudándome a encontrar y corregir un error. if(x <- 1) 1 else xno arroja un error y es muy confuso.
Gregor Thomas
3
Es decir, una realidad corrector de error útiles arrojaría un error y decir "tiene código inútil que siempre va a devolver el elsevalor, ¿se refiere a escribir de esa manera?", Pero, que puede ser una quimera ...
TylerH
26

Los operadores <-y =asignan en el entorno en el que se evalúan. El operador <-se puede usar en cualquier lugar, mientras que el operador =solo se permite en el nivel superior (por ejemplo, en la expresión completa escrita en el símbolo del sistema) o como una de las subexpresiones en una lista de expresiones entre paréntesis.

Haim Evgi
fuente
8
Creo que "nivel superior" significa en el nivel de declaración, en lugar del nivel de expresión. Entonces, x <- 42por sí solo, es una declaración; en if (x <- 42) {}sería una expresión, y no es válida. Para ser claros, esto no tiene nada que ver con si estás en el entorno global o no.
Steve Pitchers
1
Esto: "el operador = solo está permitido en el nivel superior" es un malentendido ampliamente entendido y completamente erróneo.
Konrad Rudolph el
Esto no es cierto, por ejemplo, esto funciona, aunque la asignación no es una expresión completa:1 + (x = 2)
Pavel Minaev
1
Para aclarar los comentarios de KonradRudolph y PavelMinaev, creo que es demasiado fuerte decir que está completamente equivocado, pero hay una excepción, que es cuando está "aislado de la estructura lógica circundante, con llaves o un par de paréntesis extra".
Aaron dejó Stack Overflow el
O en function() x = 1, repeat x = 1, if (TRUE) x = 1....
Moody_Mudskipper
6

Esto también puede ayudar a comprender la diferencia entre esos dos operadores:

df <- data.frame(
      a = rnorm(10),
      b <- rnorm(10)
)

Para el primer elemento, R ha asignado valores y nombre propio, mientras que el nombre del segundo elemento parece un poco extraño.

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 versión 3.3.2 (31/10/2016); macOS Sierra 10.12.1

Denis Rasulev
fuente
66
¿Puedes dar una explicación más detallada de por qué sucede esto / qué está pasando aquí? (sugerencia: data.frameintenta usar el nombre de la variable proporcionada como el nombre del elemento en el marco de datos)
Ben Bolker
Solo pensé, ¿podría esto ser un error? Y si es así, ¿cómo y dónde lo informo?
Denis Rasulev
77
No es un error. Traté de insinuar la respuesta en mi comentario anterior. Al establecer el nombre del elemento, R usará el equivalente de make.names("b <- rnorm(10)").
Ben Bolker