Scoping and evaluating functions in R
Asked Answered
L

1

8

Given the following function

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

If one runs the following code in R why is the answer 10? I'm a little confused about how y plays into this question.

z <- 10
f(3)
Leander answered 12/3, 2020 at 22:4 Comment(5)
It is getting the 'z' from within the function envCatania
Isn't the function just doing 4 + 2*x in the end? Where 4 = zMarko
@Marko Yes, but the question is, why is z = 4? It could be reasonable to expect that the function definition captures z = 10.Chiro
@RobertDodier It checks for the variables inside the first env, i.e. within the function, finds it and stop looking else where i.e. in the parent env. You can test by renaming the 'z' within the function i.e. z1 <- 4 within function and f(3)# [1] 16Catania
Or specify the environment i.e. z <- 4; environment(g) <- .GlobalEnv and then call z <- 10 > f(3) [1] 16Catania
T
9

R uses lexical scoping which means that if an object is referenced but not defined in a function then it is looked for in the environment in which the function is defined, not the environment from which it was called.

z is referenced in g but is not defined in g so it looks to the environment in which g is defined and that is the environment within f so g uses z = 4.

Actually in this case the environment that g is defined in is the same as the environment from which g is called so any way you look at it z = 4 must be used. If functions defaulted to the global environment to look for objects not defined in the function then it would use z = 10 but that is not how R works.

Making it work differently

If for some reason you wanted to force g to look for z in the environment in which f is called then you could do this (where parent.frame() refers to the environment from which f is called).

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

or we could use y + envir$z except that that would only look in the parent frame and not in its ancestors whereas with will look in ancestors of the parent frame if not found in the parent frame.

An alternative is to change g's environment like this so that it looks into envir for objects not found in 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
Tael answered 12/3, 2020 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.