transformar - En R, ¿cómo hacer que las variables dentro de una función estén disponibles para la función de nivel inferior dentro de esta función?(Con, adjuntar, entorno)
transformar variables en r (3)
Actualización 2 @G. Grothendieck publicó dos enfoques. El segundo es cambiar el entorno de la función dentro de una función. Esto resuelve mi problema de demasiadas repeticiones de codificación. No estoy seguro de si este es un buen método para pasar la verificación de CRAN al convertir mis scripts en un paquete. Voy a actualizar de nuevo cuando tenga algunas conclusiones.
Actualizar
Estoy tratando de pasar muchas variables de argumento de entrada a f2
y no quiero indexar cada variable dentro de la función como env$c, env$d, env$calls
, es por eso que intenté usar with
en f5
y f6
( una f2
modificada). Sin embargo, assign
no funciona con dentro de {}
, mover assign
fuera with
hará el trabajo, pero en mi caso real tengo algunas assign
dentro de las expresiones with
que no sé cómo sacarlas de la función with
fácilmente.
Aquí hay un ejemplo:
## In the <environment: R_GlobalEnv>
a <- 1
b <- 2
f1 <- function(){
c <- 3
d <- 4
f2 <- function(P){
assign("calls", calls+1, inherits=TRUE)
print(calls)
return(P+c+d)
}
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f2(P=0)
c <- c+1
d <- d+1
}
return(v)
}
f1()
La función f2
está dentro de f1
, cuando se llama a f2
, busca las calls,c,d
variables calls,c,d
en el entorno de environment(f1)
. Esto es lo que quería.
Sin embargo, cuando quiero usar f2
también en las otras funciones, definiré esta función en el entorno global, en cambio, la llamaré f4
.
f4 <- function(P){
assign("calls", calls+1, inherits=TRUE)
print(calls)
return(P+c+d)
}
Esto no funcionará, porque buscará las calls,c,d
en el entorno global en lugar de dentro de una función donde se llama la función. Por ejemplo:
f3 <- function(){
c <- 3
d <- 4
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f4(P=0) ## or replace here with f5(P=0)
c <- c+1
d <- d+1
}
return(v)
}
f3()
La forma segura debe ser definir calls,c,d
en los argumentos de entrada de f4
y luego pasar estos parámetros a f4
. Sin embargo, en mi caso, hay demasiadas variables para pasar a esta función f4
y sería mejor que pueda pasarlo como entorno y decirle a f4
que no mire en el entorno global ( environment(f4)
), solo busque Dentro del environment
cuando se llama f3
.
La forma en que lo resuelvo ahora es usar el entorno como una lista y usar la función with
.
f5 <- function(P,liste){
with(liste,{
assign("calls", calls+1, inherits=TRUE)
print(calls)
return(P+c+d)
}
)
}
f3 <- function(){
c <- 3
d <- 4
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f5(P=0,as.list(environment())) ## or replace here with f5(P=0)
c <- c+1
d <- d+1
}
return(v)
}
f3()
Sin embargo, ahora assign("calls", calls+1, inherits=TRUE)
no funciona como debería ya que assign
no modifica el objeto original. Las calls
variables están conectadas a una función de optimización donde la función objetivo es f5
. Esa es la razón por la que utilizo assign
lugar de pasar calls
como argumentos de entrada. El uso de attach
tampoco es claro para mí. Aquí está mi manera de corregir el problema de assign
:
f7 <- function(P,calls,liste){
##calls <<- calls+1
##browser()
assign("calls", calls+1, inherits=TRUE,envir = sys.frame(-1))
print(calls)
with(liste,{
print(paste(''with the listed envrionment, calls='',calls))
return(P+c+d)
}
)
}
########
##################
f8 <- function(){
c <- 3
d <- 4
calls <- 0
v <- vector()
for(i in 1:10){
##browser()
##v[i] <- f4(P=0) ## or replace here with f5(P=0)
v[i] <- f7(P=0,calls,liste=as.list(environment()))
c <- c+1
d <- d+1
}
f7(P=0,calls,liste=as.list(environment()))
print(paste(''final call number'',calls))
return(v)
}
f8()
No estoy seguro de cómo se debe hacer esto en R. ¿Estoy en la dirección correcta, especialmente al pasar por el control CRAN? ¿Alguien tiene algunos consejos sobre esto?
En general, diría que cualquier variable que sea necesaria dentro de una función debe transmitirse a través de sus argumentos. Además, si su valor se necesita más adelante, se lo devolverá de la función. No hacer esto puede conducir rápidamente a resultados extraños, por ejemplo, si hay varias funciones que definen una variable x
, cuál debe usarse. Si la cantidad de variables es mayor, creará una estructura de datos personalizada, por ejemplo, colocándolas en una lista con nombre.
También se podría usar una función que redefine otras funciones en el entorno especificado.
test_var <- "global"
get_test_var <- function(){
return(test_var)
}
some_function <- function(){
test_var <- "local"
return(get_test_var())
}
some_function() # Returns "global". Not what we want here...
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
some_function2 <- function(){
test_var <- "local"
# define function locally
get_test_var2 <- function(){
return(test_var)
}
return(get_test_var2())
}
some_function2() # Returns "local", but ''get_test_var2'' can''t be used in other places.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
add_function_to_envir <- function(my_function_name, to_envir) {
script_text <- capture.output(eval(parse(text = my_function_name)))
script_text[1] <- paste0(my_function_name, " <- ", script_text[1])
eval(parse(text = script_text), envir = to_envir)
}
some_function3 <- function(){
test_var <- "local"
add_function_to_envir("get_test_var", environment())
return(get_test_var())
}
some_function3() # Returns "local" and we can use ''get_test_var'' from anywhere.
Aquí add_function_to_envir(my_function_name, to_envir)
captura el script de la función, lo analiza y lo reevalúa en el nuevo entorno.
Nota: el nombre de la función para my_function_name
debe estar entre comillas.
(1) Entorno de la persona que llama. Puede pasar explícitamente el entorno padre e indexarlo. Prueba esto:
f2a <- function(P, env = parent.frame()) {
env$calls <- env$calls + 1
print(env$calls)
return(P + env$c + env$d)
}
a <- 1
b <- 2
# same as f1 except f2 removed and call to f2 replaced with call to f2a
f1a <- function(){
c <- 3
d <- 4
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f2a(P=0)
c <- c+1
d <- d+1
}
return(v)
}
f1a()
(2) Restablecer el entorno de la función llamada es restablecer el entorno de f2b
en f1b
como se muestra aquí:
f2b <- function(P) {
calls <<- calls + 1
print(calls)
return(P + c + d)
}
a <- 1
b <- 2
# same as f1 except f2 removed, call to f2 replaced with call to f2b
# and line marked ## at the beginning is new
f1b <- function(){
environment(f2b) <- environment() ##
c <- 3
d <- 4
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f2b(P=0)
c <- c+1
d <- d+1
}
return(v)
}
f1b()
(3) Macro usando eval.parent (sustituto (...)) Otro enfoque es definir una construcción de tipo macro que inyecta efectivamente el cuerpo de f2c
en línea en f1c1
. Aquí f2c
es lo mismo que f2b
excepto por las calls <- calls + 1
line (no <<-
needed) y el eval.parent(substitute({...}))
todo el cuerpo en eval.parent(substitute({...}))
. f1c
es lo mismo que f1a
excepto que la llamada a f2a
se reemplaza con una llamada a f2c
.
f2c <- function(P) eval.parent(substitute({
calls <- calls + 1
print(calls)
return(P + c + d)
}))
a <- 1
b <- 2
f1c <- function(){
c <- 3
d <- 4
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f2c(P=0)
c <- c+1
d <- d+1
}
return(v)
}
f1c()
(4) defmacro Esto es casi lo mismo que la última solución, excepto que usa defmacro
en el paquete gtools para definir la macro en lugar de hacerlo nosotros mismos. (También vea el paquete Rcmdr para otra versión de defmacro). Debido a la forma en que funciona defmacro
, también debemos pasar calls
pero como es una macro y no una función, esto solo le dice que sustituya las calls
y no es lo mismo que pasar calls
a un función.
library(gtools)
f2d <- defmacro(P, calls, expr = {
calls <- calls + 1
print(calls)
return(P + c + d)
})
a <- 1
b <- 2
f1d <- function(){
c <- 3
d <- 4
calls <- 0
v <- vector()
for(i in 1:10){
v[i] <- f2d(P=0, calls)
c <- c+1
d <- d+1
}
return(v)
}
f1d()