Es probable que las personas que han estado usando el lenguaje R durante un período de tiempo se hayan acostumbrado a pasar características como argumentos a otras funciones. Sin embargo, es mucho menos probable que las personas regresen funciones de su propio código personalizado. Esto es simplemente demasiado horrible porque hacerlo puede abrir un mundo completamente nuevo de abstracción que puede reducir en gran medida la cantidad y la complejidad del código necesario para completar ciertos tipos de funciones. Aquí ofrecemos algunos ejemplos breves de las formas en que los programadores de R pueden hacer uso de cierres léxicos para encapsular tanto registros como estrategias.
Implementación en R
Para empezar, una instancia fácil, suponga que desea una función que proporcione add_2() a su argumento. Probablemente podrías escribir algo como esto:
R
add_2 <- function(y) { 2 + y }
Que hace exactamente lo que esperas:
> add_2(1:10) [1] 3 4 5 6 7 8 9 10 11 12
Ahora suponga que necesita cualquier otra característica que proporcione 7 a su argumento. Lo mejor que puedes hacer es anotar cualquier otra característica, como add_2 , donde el 2 se reemplaza por un 7. Pero esto sería muy ineficaz: si en el futuro descubres que cometiste un error y en verdad Si desea multiplicar los valores en lugar de agregarlos, se verá presionado para cambiar el código en algunos lugares. En este caso trivial, eso no será una gran molestia, pero para proyectos más complicados, la duplicación de código es una receta para la catástrofe. Un concepto superior podría ser poner por escrito una característica que toma un argumento, x, que devuelve todas las demás funciones que proporcionan su argumento, y, a x. En otras palabras, algo como esto:
R
add_x <- function(x) { function(y) { x + y } }
Ahora, mientras nombra add_x con un argumento, puede obtener una función que hace exactamente lo que necesita:
R
add_2 <- add_x(2) add_7 <- add_x(7)
> add_2(1:10) [1] 3 4 5 6 7 8 9 10 11 12 > add_7(1:10) [1] 8 9 10 11 12 13 14 15 16 17
Así que esto no parece demasiado trascendental. Pero si observa detenidamente la definición de add_x, puede notar algo extraño: ¿cómo se da cuenta la característica de retorno para encontrar x cuando se menciona en un punto posterior?
Resulta que R tiene un alcance léxico, lo que significa que las características brindan una conexión con el entorno en el que se describieron. En este caso, cuando llama a add_x , el argumento x que ofrece se adjunta al entorno para la característica de devolución. En otras palabras, en este ejemplo simple, puede pensar en R como simplemente cambiar todas las instancias de la variable x dentro de la función para que estén más abajo con el valor que especifique mientras conoce como add_x. Ok, entonces esto puede ser un buen truco, sin embargo, ¿cómo se puede usar esto de manera más productiva? Para una instancia un poco más complicada, piense que está haciendo un arranque complejo y, para mayor eficiencia, asigna previamente vectores de contenedores para mantener los resultados. Esto es fácil si tiene un solo vector de efectos; todo lo que necesita hacer es tener en cuenta para iterar un contador de índice cada vez que cargue un resultado final en el vector.
R
for (i in 1:nboot) { bootmeans[i] <- mean(sample(data, length(data), replace = TRUE)) }
> mean(data) [1] 0.0196 > mean(bootmeans) [1] 0.0188
Pero piense que necesita realizar un seguimiento de varias estadísticas extraordinarias, cada una de las cuales requiere que realice un seguimiento de una variable de índice única. Si su rutina de arranque es un poco complicada, esto podría ser tedioso y vulnerable a errores. Mediante el uso de cierres, puede resumir toda esta contabilidad. Aquí hay una función constructora que envuelve un vector de contenedor preasignado:
R
make_container <- function(n) { x <- numeric(n) i <- 1 function(value = NULL) { if (is.null(value)) { return(x) } else { x[i] <<- value i <<- i + 1 } } }
Cuando llama a make_container con un problema, asigna previamente un vector numérico del período especificado, n, y devuelve una función que le permite presentar estadísticas para ese vector sin tener que preocuparse por mantener la música de un índice. Si no lo hace, el argumento de esa función de retorno es NULL, el vector completo es la parte inferior de la espalda.
R
bootmeans <- make_container(nboot) for (i in 1:nboot) bootmeans(mean(sample(data, length(data), replace = TRUE)))
> mean(data) [1] 0.0196 > mean(bootmeans()) [1] 0.0207
Aquí make_container es tremendamente fácil, pero puede ser tan complicado como necesites. Por ejemplo, es posible que desee que la función constructora realice algunos cálculos costosos que, en cambio, ya no podría hacer en cada ocasión en que se conoce al personaje. De hecho, eso es lo que incluso he hecho en el paquete boolean3 para reducir el número de cálculos realizados en cada nueva versión de la optimización habitual.
Publicación traducida automáticamente
Artículo escrito por ritikkumartiwari y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA