Reputation: 822
I try to understand closures in R.
Can someone explain me why this code (1):
vars <- c("one", "two")
funcs <- list()
for (v in vars) {
funcs[[length(funcs) + 1]] <- function() {
print(v)
}
}
funcs[[1]]()
funcs[[2]]()
returns
[1] "two"
[1] "two"
but following code (2):
vars <- c("one", "two")
funcs <- list()
getFunc <- function(v) {
v_i <- v
function() {
print(v_i)
}
}
for (v in vars) {
funcs[[length(funcs) + 1]] <- getFunc(v)
}
funcs[[1]]()
funcs[[2]]()
returns:
[1] "one"
[1] "two"
As addition, this code still prints only "two" (3):
vars <- c("one", "two")
funcs <- list()
for (v in vars) {
v_i <- v
funcs[[length(funcs) + 1]] <- function() {
print(v_i)
}
}
funcs[[1]]()
funcs[[2]]()
And this also prints only "two" (4):
vars <- c("one", "two")
funcs <- list()
getFunc <- function(v) {
function() {
print(v)
}
}
for (v in vars) {
funcs[[length(funcs) + 1]] <- getFunc(v)
}
funcs[[1]]()
funcs[[2]]()
I am guessing that v is reused so it points always to the same space in memory. But can we be sure that v will always be the last value of the list - it would persist in time or it is volatile?
(3) mean that is not enough to assign value to other variable it has to be in separate function.
(4) mean that argument of function is also reused.
Upvotes: 2
Views: 74
Reputation: 269556
The v
in the global environment only has a single value at any one time but as the for
loop changes the value of v
changes. On the first iteration v
is "one"
and on the second iteration it is "two"
and since v
is not subsequently changed it stays at the value "two"
. Now,
1) In the first case when you call funcs[[1]]()
it looks for v
in funcs[[1]]
and can't find it so it looks in the environment(funcs[[1]])
and that is the global environment.
environment(funcs[[1]])
## <environment: R_GlobalEnv>
The value of v
in the global environment at the time that funcs[[1]]
is run is "two" so it prints that.
2) In the second example when you call funcs[[1]]()
it looks for v_i
in funcs[[1]]
and can't find it so again it looks in environent(funcs[[1]])
but now the environment of funcs[[1]]
is the runtime environment within getfunc
.
Each time getfunc
is run that environment will be different. Anyways within that environment v_i
has the value "one"
so it prints that.
environment(funcs[[1]])
## <environment: 0x00000000097418d0>
3) The third example is basically the same situation as the first. v_i
is in the global environment and has the value "two"
at the time funcs[[1]()
is run.
4) In the fourth example the environment(funcs[[1]])
is the runtime environment within getfunc
; however, because the v
argument to getfunc
has not been accessed prior to funcs[[1]]
being called, v
is still an unevaluated promise. When funcs[[1]]
is finally called it needs to use v
the promise is evaluated and at that point v
is "two"
.
Upvotes: 2