¿Cómo se usa "< < - " (asignación de alcance) en R?


Acabo de terminar de leer sobre alcance en la introducción R, y soy muy curioso acerca de la asignación <<-.

El manual mostró un ejemplo (muy interesante) para <<-, que siento que entendí. Lo que todavía me falta es el contexto de cuándo esto puede ser útil.

Así que lo que me encantaría leer de usted son ejemplos (o enlaces a ejemplos) sobre cuándo el uso de <<- puede ser interesante/útil. ¿Cuáles podrían ser los peligros de usarlo (parece fácil perder la pista de), y cualquier consejo que quieras compartir.

Author: Bhargav Rao, 2010-04-13

6 answers

<<- es más útil en conjunción con cierres para mantener el estado. He aquí una sección de un documento reciente mío:

Un cierre es una función escrita por otra función. Los cierres se llaman así porque encierran el entorno de la función padre, y pueden acceder a todas las variables y parámetros en esa función. Esto es útil porque nos permite tener dos niveles de parámetros. Un nivel de parámetros (el padre) controla cómo funciona la función. El otro level (el niño) hace el trabajo. El siguiente ejemplo muestra cómo se puede utilizar esta idea para generar una familia de funciones de potencia. La función padre (power) crea funciones hijas (square y cube) que realmente hacen el trabajo duro.

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 capacidad de administrar variables en dos niveles también hace posible mantener el estado a través de invocaciones de funciones al permitir que una función modifique variables en el entorno de su padre. Clave para la gestión de variables en diferentes niveles es el operador de asignación de flecha doble <<-. A diferencia de la asignación de flecha única habitual (<-) que siempre funciona en el nivel actual, el operador de flecha doble puede modificar variables en los niveles principales.

Esto hace posible mantener un contador que registra cuántas veces se ha llamado a una función, como muestra el siguiente ejemplo. Cada vez que se ejecuta new_counter, crea un entorno, inicializa el contador i en este entorno, y luego crea una nueva función.

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

La nueva función es un closure, y su entorno es el entorno envolvente. Cuando se ejecutan los cierres counter_one y counter_two, cada uno modifica el contador en su entorno envolvente y luego devuelve el recuento actual.

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

counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
 148
Author: hadley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-28 12:09:40

Ayuda pensar en <<- como equivalente a assign (si establece el parámetro inherits en esa función a TRUE). El beneficio de assign es que le permite especificar más parámetros (por ejemplo, el entorno), por lo que prefiero usar assign sobre <<- en la mayoría de los casos.

Usando <<- y assign(x, value, inherits=TRUE) significa que "los entornos que encierran el entorno suministrado se buscan hasta que se encuentra la variable 'x'."En otras palabras, seguirá pasando por los entornos en orden hasta que encuentra una variable con ese nombre, y la asignará a eso. Esto puede estar dentro del alcance de una función, o en el entorno global.

Para entender lo que hacen estas funciones, también necesita entender los entornos R (por ejemplo, usando search).

Utilizo regularmente estas funciones cuando estoy ejecutando una simulación grande y quiero guardar resultados intermedios. Esto le permite crear el objeto fuera del ámbito de la función dada o apply bucle. Eso es muy útil, especialmente si tiene alguna preocupación sobre un bucle grande que termina inesperadamente (por ejemplo, una desconexión de la base de datos), en cuyo caso podría perder todo en el proceso. Esto sería equivalente a escribir sus resultados en una base de datos o archivo durante un proceso de larga ejecución, excepto que está almacenando los resultados dentro del entorno R en su lugar.

Mi advertencia principal con esto: tenga cuidado porque ahora está trabajando con variables globales, especialmente cuando se usa <<-. Eso significa que puede terminar con situaciones en las que una función está utilizando un valor de objeto del entorno, cuando esperaba que estuviera utilizando uno que se suministró como parámetro. Esta es una de las principales cosas que la programación funcional intenta evitar (ver efectos secundarios). Evito este problema asignando mis valores a nombres de variable únicos (usando pegar con un conjunto o parámetros únicos) que nunca se usan dentro de la función, sino que solo se usan para almacenar en caché y en caso de que necesite recuperarse más tarde (o hacer algún metanálisis de los resultados intermedios).

 29
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-13 13:59:05

Un lugar donde usé <<- fue en GUI simples usando tcl/tk. Algunos de los ejemplos iniciales lo tienen as ya que necesita hacer una distinción entre variables locales y globales para la integridad del estado. Véase por ejemplo

 library(tcltk)
 demo(tkdensity)

Que utiliza <<-. De lo contrario, estoy de acuerdo con Marek:) a una búsqueda en Google puede ayudar.

 5
Author: Dirk Eddelbuettel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-13 12:12:30
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")
 4
Author: lcgong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-13 12:23:22

El operador <<- también puede ser útil para las Clases de Referencia al escribir Métodos de Referencia. Por ejemplo:

myRFclass <- setRefClass(Class = "RF",
                         fields = list(A = "numeric",
                                       B = "numeric",
                                       C = function() A + B))
myRFclass$methods(show = function() cat("A =", A, "B =", B, "C =",C))
myRFclass$methods(changeA = function() A <<- A*B) # note the <<-
obj1 <- myRFclass(A = 2, B = 3)
obj1
# A = 2 B = 3 C = 5
obj1$changeA()
obj1
# A = 6 B = 3 C = 9
 2
Author: Carlos Cinelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-15 15:12:24

Sobre este tema me gustaría señalar que el operador

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

Puede esperar que la función devuelva la suma esperada, 6, pero en su lugar devuelve 0, con una variable global mySum creada y asignada al valor 3. No puedo explicar completamente lo que está pasando aquí, pero ciertamente el cuerpo de un bucle for es no un nuevo 'nivel'de alcance. En cambio, parece que R se ve fuera de la función fortest, no puede encontrar una variable mySum para asignar, por lo que crea una y asigna el valor 1, la primera vez a través del bucle. En iteraciones posteriores, el RHS en la asignación debe referirse a la variable interna mySum (sin cambios), mientras que el LHS se refiere a la variable global. Por lo tanto, cada iteración sobrescribe el valor de la variable global al valor de esa iteración de i, por lo tanto, tiene el valor 3 al salir del función.

Espero que esto ayude a alguien - esto me dejó perplejo por un par de horas hoy! (Por cierto, simplemente reemplace

 2
Author: Matthew Wise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-03 20:50:12