Funciones de alcance y evaluación en R

8

Dada la siguiente función

f <- function(x) {
    g <- function(y) {
            y + z
    }
    z <- 4
    x + g(x)
 }

Si uno ejecuta el siguiente código en R, ¿por qué la respuesta es 10? Estoy un poco confundido acerca de cómo juega con esta pregunta.

z <- 10
f(3)
nzhanggh
fuente
2
Está obteniendo la 'z' desde la función env
akrun
55
¿Acaso la función no está funcionando al 4 + 2*xfinal? Donde 4 = z
markus
1
@markus Sí, pero la pregunta es, ¿por qué z = 4? Podría ser razonable esperar que la definición de la función capture z = 10.
Robert Dodier
@RobertDodier Comprueba las variables dentro del primer entorno, es decir, dentro de la función, lo encuentra y deja de buscar en otro lugar, es decir, en el entorno principal. Puede probar cambiando el nombre de la 'z' dentro de la función, es decir, z1 <- 4dentro de la función yf(3)# [1] 16
akrun
O especifique el entorno, es decir, z <- 4; environment(g) <- .GlobalEnvy luego llamez <- 10 > f(3) [1] 16
akrun

Respuestas:

9

R utiliza el alcance léxico, lo que significa que si se hace referencia a un objeto pero no se define en una función, se busca en el entorno en el que se define la función , no en el entorno desde el que se llamó.

Se hace referencia a z en g pero no se define en g, por lo que mira al entorno en el que se define g y ese es el entorno dentro de f, entonces g usa z = 4.

En realidad, en este caso, el entorno en el que se define g es el mismo que el entorno desde el que se llama g, por lo que debe usarse z = 4 de cualquier forma que lo mire. Si las funciones predeterminadas al entorno global para buscar objetos no definidos en la función, entonces usaría z = 10, pero no es así como funciona R.

Hacer que funcione de manera diferente

Si por alguna razón quisiera forzar a g a buscar z en el entorno en el que se llama f, entonces podría hacerlo (donde se parent.frame()refiere al entorno desde el que se llama f).

f2 <- function(x, envir = parent.frame()) {
    g <- function(y) {
            y + with(envir, z)
    }
    z <- 4
    x + g(x)
 }
 z <- 10
 f2(3)
 ## [1] 16

o podríamos usar y + envir$z excepto que eso solo se vería en el marco primario y no en sus antepasados, mientras que withse vería en ancestros del marco primario si no se encuentra en el marco primario.

Una alternativa es cambiar el entorno de g de esta manera para que busque envirobjetos que no se encuentran en g:

f3 <- function(x, envir = parent.frame()) {
    g <- function(y) {
            y + z
    }
    environment(g) <- envir
    z <- 4
    x + g(x)
 }
 z <- 10
 f3(3)
 ## [1] 16
G. Grothendieck
fuente