¿Cuáles son las diferencias entre “=” y “<-" en R?

¿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 esta es la única diferencia?

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

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

En este caso, x se declara dentro del scope 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, x se 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 las firmas de funciones) para la compatibilidad con (muy) versiones 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 R IDE tienen atajos de teclado para hacer <- más fácil de escribir. Ctrl + = en Arquitecto, Alt + - en RStudio ( Opción + - bajo macOS), Mayús + - (guión bajo) en emacs + ESS.


Si prefiere escribir = a <- pero desea usar el símbolo de asignación más común para el código de publicación pública (en CRAN, por ejemplo), entonces puede usar una de las funciones tidy_* en el paquete formatR 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 = 5 arroja un error pero no x <- y <- 5 ?" es "depende de la magia contenida en el analizador". La syntax 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 <- .

Para comprender lo que está sucediendo, necesita saber que la asignación devuelve silenciosamente el valor que se le asignó. Puede ver eso más claramente al imprimir 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. Asi que

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

El analizador interpreta x <- y <- 5 como

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

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

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

pero en realidad se interpreta como

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

Esto se debe a que = es menor que <- , como se muestra en la página de ayuda de " ?Syntax .

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 buen detalle en los 5 operadores de asignación.

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

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 progtwigción sea ilegal.

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

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

Puede lograr esto si está “aislado de la estructura lógica circundante, mediante llaves o un par adicional de paréntesis”, de modo que if ((x = 0)) 1 else x funcionaría.

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

x = y = 5 es 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 asigne ese 5 a x .

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

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

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

Ver ?Syntax y ?assignOps para las ?assignOps de precedencia (enlace) y de agrupamiento.

Los operadores <- y = asignan al entorno en el que se evalúan. El operador <- puede usarse 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 corregidas.

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

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

 … '-> ->>' rightwards assignment '<- <<-' assignment (right to left) '=' assignment (right to left) … 

¿Pero esta es la única diferencia?

Ya que preguntaba sobre los operadores de asignación : sí, esa es la única diferencia. Sin embargo, serás perdonado por creer lo contrario. Incluso la documentación R de ?assignOps afirma que hay más diferencias:

El operador <- puede usarse 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 corregidas.

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 arriostradas (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, usando = , fuera de los contextos (a) y (b). Entonces, ¿por qué la documentación de una función básica del lenguaje R ha sido incorrecta durante décadas?

Es porque en la syntax de R, el símbolo = tiene dos significados distintos que se combinan rutinariamente:

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

Veamos.

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

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

... the = es el token que define el paso del argumento con nombre: no es el operador de asignación. Además, = está totalmente 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 ".

En cualquier otro contexto, = refiere a la llamada del operador de asignación. En particular, simplemente poniendo 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 función base::file.copy (reemplazando <- with = ) - es un patrón generalizado en gran parte de la base del código R central.

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

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


Una confesión: mentí antes. Hay una diferencia adicional entre los operadores = y <- : llaman 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 distintos, siempre llaman a la misma función. Anular uno también anula al otro. Sabiendo que esto es raramente práctico, pero puede ser utilizado para algunas travesuras divertidas .

Esto también puede boost la comprensión de la diferencia entre esos dos operadores:

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

Para el primer elemento R tiene valores asignados 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 (2016-10-31); macOS Sierra 10.12.1