The functional environment in the lapply circuit

so I ran into this problem related to variables existing in different environments and it confused me very much as it doesn't fit my understanding of how functions look for different objects.

my toy example is very simple: I have a function foo

with one argument j

. foo

lives inside a loop function lapply

with an 'i' argument. now i

clearly exists in the environment lapply

(and is not included in the global). when called inside the lapply function foo

, it tries to find i

, as well as errors and errors:

foo <- function(j){
 message('foo env: exists(j) ', exists('j'))
 message('foo env: exists(i) ', exists('i'))
 i
}

env.g <- environment()
invisible(lapply(1, FUN = function(i){
  message('global env: exists(i) ', exists('i', envir = env.g))
  message('lapply env: exists(i) ', exists('i'))
  message(' ')
  j <- i + 1

  foo(j)
   }
  ))

#global env: exists(i) FALSE
#lapply env: exists(i) TRUE

#foo env: exists(j) TRUE
#foo env: exists(i) FALSE
#Error in foo(j) : object 'i' not found

      

when, on the other hand, i

exists in the global environment, foo

okay with this:

i <- 10
foo()
#foo env: exists(j) TRUE
#foo env: exists(i) TRUE
#[1] 10

      

so my previous understanding was that if a function doesn't see a variable in its own environment, it moves on to the next one ( lapply

in my first example and a global env. in my second) until it finds It. however it does not explicitly go into the outer loop lapply

in the above ... why?

+3


source to share


2 answers


I believe this is because the function foo()

is being evaluated in the environment in which it is defined. In your example, it is foo()

defined in the global environment and is therefore i

not included in the scope. If you define foo()

inside an anonymous function, then it i

appears to be correctly evaluated.



env.g <- environment()
invisible(lapply(1, FUN = function(i){
  message('global env: exists(i) ', exists('i', envir = env.g))
  message('lapply env: exists(i) ', exists('i'))
  message(' ')
  j <- i + 1

  foo <- function(j){
   message('foo env: exists(j) ', exists('j'))
   message('foo env: exists(i) ', exists('i'))
   i
  }

  foo(j)
   }
  ))

#global env: exists(i) FALSE
#lapply env: exists(i) TRUE

#foo env: exists(j) TRUE
#foo env: exists(i) TRUE

      

+2


source


There are 4 types of environments associated with a function.

At startup:

rm(i)
lapply(1, foo)

      

or even:

rm(i)
lapply(1, function(x) {
  i <- 42
  foo(x)
})

      

the situation is like this:



lapply

:

  • Enclosing env: namespace:base

  • Env binding: package:base

  • Env execution: created on the fly and enclosed in .GlobalEnv

  • Calling env: .GlobalEnv

and foo

:

  • Enclosing (where it was defined): .GlobalEnv

  • Binding (where name foo

    is):.GlobalEnv

  • Execution (enclosed in Calling env): created on the fly and enclosed in an application ... I'm not even sure where, but going up the chain of environments there should be a runtime lapply

  • Challenge: same, not really sure ... but that shouldn't matter

But:
(Possibly) contrary to intuition, the variables are not discovered by going to the dynamic scoping AKA "call stack" (this would be: exec env of foo

(then maybe some staging environments), then exec env lapply

(where we find i <- 42

), then .GlobalEnv

). but directly in the foo

AKA environment of lexical scope , then the chain of its closed environments, thereby bypassing the runtime lapply

and thus not finding it i

, even if it is explicitly declared just above ...

0


source







All Articles