<<-
es más útil junto con cierres para mantener el estado. Aquí hay una sección de un artículo mío reciente:
Un cierre es una función escrita por otra función. Los cierres se denominan así porque encierran el entorno de la función principal y pueden acceder a todas las variables y parámetros de 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 nivel (el niño) hace el trabajo. El siguiente ejemplo muestra cómo puede usar esta idea para generar una familia de funciones de poder. La función principal ( power
) crea funciones secundarias ( 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 en las invocaciones de funciones al permitir que una función modifique variables en el entorno de su padre. La clave para administrar variables en diferentes niveles es el operador de asignación de doble flecha <<-
. A diferencia de la asignación de flecha única habitual ( <-
) que siempre funciona en el nivel actual, el operador de flecha doble puede modificar las variables en los niveles principales.
Esto permite mantener un contador que registra cuántas veces se ha llamado a una función, como muestra el siguiente ejemplo. Cada vez que new_counter
se ejecuta, 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 cierre, 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 cerrado y luego devuelve el conteo actual.
counter_one <- new_counter()
counter_two <- new_counter()
counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
Es útil pensar que
<<-
es equivalente aassign
(si establece elinherits
parámetro en esa función enTRUE
). La ventaja deassign
es que le permite especificar más parámetros (por ejemplo, el medio ambiente), así que prefiero utilizarassign
más<<-
en la mayoría de los casos.El uso de
<<-
yassign(x, value, inherits=TRUE)
significa que "los entornos del entorno suministrado se buscan hasta que se encuentra la variable 'x'". En otras palabras, seguirá pasando por los entornos en orden hasta que encuentre una variable con ese nombre, y se la asignará a eso. Esto puede estar dentro del alcance de una función, o en el entorno global.Para comprender lo que hacen estas funciones, también debe comprender los entornos R (por ejemplo, el uso
search
).Regularmente uso estas funciones cuando ejecuto una simulación grande y quiero guardar resultados intermedios. Esto le permite crear el objeto fuera del alcance de la función o
apply
bucle dado . Eso es muy útil, especialmente si le preocupa que un ciclo grande termine 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.Mi advertencia principal con esto: tenga cuidado porque ahora está trabajando con variables globales, especialmente cuando lo usa
<<-
. Eso significa que puede terminar con situaciones en las que una función está usando un valor de objeto del entorno, cuando esperaba que estuviera usando uno que se proporcionó como parámetro. Esta es una de las cosas principales que la programación funcional intenta evitar (ver efectos secundarios ). Evito este problema asignando mis valores a nombres de variables únicos (usando pegar con un conjunto o parámetros únicos) que nunca se usan dentro de la función, pero solo se usan para el almacenamiento en caché y en caso de que necesite recuperarme más tarde (o hacer algún meta -análisis sobre los resultados intermedios).fuente
Un lugar donde lo usé
<<-
fue en GUI simples usando tcl / tk. Algunos de los ejemplos iniciales lo tienen, ya que necesita hacer una distinción entre las variables locales y globales para la capacidad de estado. Ver por ejemploque usos
<<-
. De lo contrario, estoy de acuerdo con Marek :): una búsqueda en Google puede ayudar.fuente
tkdensity
en R 3.6.0.fuente
<<-
. Un bucle for sería más claro en este caso.Sobre este tema, me gustaría señalar que el
<<-
operador se comportará de manera extraña cuando se aplique (incorrectamente) dentro de un bucle for (también puede haber otros casos). Dado el siguiente código:puede esperar que la función devuelva la suma esperada, 6, pero en cambio devuelve 0, con una variable global que
mySum
se crea y se le asigna el valor 3. No puedo explicar completamente lo que está sucediendo aquí, pero ciertamente el cuerpo de a para loop no es un nuevo 'nivel' de alcance. En cambio, parece que R mira fuera de lafortest
función, no puede encontrar unamySum
variable para asignar, por lo que crea una y asigna el valor 1, la primera vez a través del ciclo. En iteraciones posteriores, el RHS en la asignación debe referirse a lamySum
variable interna (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óni
, por lo tanto, tiene el valor 3 al salir de la función.Espero que esto ayude a alguien, ¡esto me dejó perplejo durante un par de horas hoy! (Por cierto, simplemente reemplace
<<-
con<-
y la función funciona como se esperaba).fuente
mySum
nunca se incrementa sino solo lo globalmySum
. Por lo tanto, en cada iteración del ciclo for, el globalmySum
obtiene el valor0 + i
. Puedes seguir esto condebug(fortest)
.<-
todas partes de manera consistente dentro de la función si solo desea actualizar la variable local dentro de la función.El
<<-
operador también puede ser útil para las clases de referencia al escribir métodos de referencia . Por ejemplo:fuente