Reputation: 5545
I have the following code
new_counter <- function(){
ij <- 0
function(){
ij <<- ij+1
ij
}
}
counter_one <- new_counter()
counter_two <- new_counter()
counter_one()
counter_one()
from this tutorial.
They write:
The counters get around the “fresh start” limitation by not modifying variables in their local environment. Since the changes are made in the unchanging parent (or enclosing) environment, they are preserved across function calls.
This is what I think is going on: The new_counter
function is created in the .GlobalEnv
(enclosing environment
) as well as the counter_one
function. But the execution environment
of new_counter
function is one level higher than the counter_one functions. This is usually temporary but this time it is not since we have an enclosure function.
But how can I see 'this' enclosing enviroment and that they are one level apart? Using (pryr) where
and environment delivers for both functions the global environment.
EDIT: They talk about two different environments:
So to me these are two different environments but if they differ they probably have a different name! How can I find out the name? Using
where("new_counter")
where("counter_one")
both deliver the global environment but looking in the enumeration above this must be two different ones.
Upvotes: 1
Views: 647
Reputation: 269556
This should hopefully clarify what is happening. Note that the enclosing environment of new_counter
is the global environment but the environments of counter_one
and counter_two
are the environments within the two execution instances of new_counter
. Normally a function's enclosing environment is its lexical environment (the environment where it is defined) but in some situations this varies.
new_counter <- function(){
e <- environment()
print(e)
ij <- 0
function(){
ij <<- ij+1
ij
}
}
counter_one <- new_counter() # A
counter_two <- new_counter() # B
counter_one()
counter_one()
environment(new_counter) # global environment
environment(counter_one) # see output of A
environment(counter_two) # see output of B
Here is a simpler example that shows that a function can be located in one environment yet its enclosing environment can be a different environment. In this case f
is in the global environment; however, its enclosing environment is e
and not the global environment.
e <- new.env()
e$x <- 1
x <- 2
f <- function() x
environment(f) <- e
f()
## [1] 1
There are actually several environments that one can consider. Let us add some instrumentation to f
in the last example.
f <- function() {
ee <- environment()
print(ee)
x
}
e <- new.env()
e$x <- 1
x <- 2
environment(f) <- e
f() # displays ee and then the value of x which is 1
These are the environments:
the enclosing environment of f
is e
since we set it that way
the lexical environment is the environment that f is located in. It is the global environment here. Usually the lexical environment and the enclosing environment are the same but because we reset the enclosing environment of f
in this case they are different.
the execution environment within the function f
while it is running. In this case it is ee
. The executation environment will be destroyed when f
completes unless there are still pointers to it from the outside. Each time f
is run a new execution environment is created so ee
takes on a new value each time f
is run.
Thus in this example there is e
, the global environment and one execution environment each time that f
is run.
There is some discussion here: http://adv-r.had.co.nz/Environments.html
Update Some clarifications.
Upvotes: 2
Reputation: 160437
Without pryr
, you can see the environments with something like:
environment(counter_one)
# <environment: 0x00000000078be648>
environment(counter_two)
# <environment: 0x00000000078bf148>
ls(env=environment(counter_two))
# [1] "ij"
counter_one()
# [1] 1
get("ij", envir = environment(counter_one))
# [1] 1
get("ij", envir = environment(counter_two))
# [1] 0
Furthermore, you can change the values of variables within that environment:
assign("abc", 123, envir = environment(counter_two))
ls(envir = environment(counter_one))
# [1] "ij"
ls(envir = environment(counter_two))
# [1] "abc" "ij"
Upvotes: 1