Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour that R retains last variable in list loop?

When I make a loop to wrap functions in a list, the last function in the input list is always used for all wrapping call.

wrapper <- function(f)function()f()
fs <- list(f = function()"call f", g = function()"call g")
ws <- list()
for(n in names(fs))
    ws[[n]] <- wrapper(fs[[n]])
ws$f()
[1] "call g"

I expected "call f" in the above code, but it returned "call g" in fact.

Could anyone explain me why this happened?

What is the sign, or in which situation I have to force evaluation to avoid the similar

Thank you very much

like image 582
transang Avatar asked Nov 07 '22 12:11

transang


1 Answers

Finally, I figured out my own answer

There are 2 reasons why this happens: for loop does not have its own environment and wrapper does lazily evaluate its parameter f.

For the first reason: for loop uses the global environment

For the latter reason: changing wrapper to function(f){force(f); function()f()} leads to my expected result in the question.

Detailed explanation:

The loop in the question is interpreted into

for(n in names(fs)){
  tmp <- fs[[n]]
  ws[[n]] <- wrapper(tmp)
}

Because wrapper does lazily evaluate parameter f, it keeps f in its execution environment but does not keep its value. Instead, it memorizes how-to-evaluate f when need. In other word, it memorizes the pair (<environment: R_GlobalEnv>, "tmp").

Checking out the following code clears everything I said.

tmp <- f
wf <- wrapper(tmp)
tmp <- g
wf()
[1] "call g"

but

tmp <- f
wf <- wrapper(tmp)
wf()
[1] "call f"
tmp <- g
wf()
[1] "call f"

The latter code printed "call f" in the second call because f is evaluated and stored in wrapper's environment and there is no need to evaluated again (to become g)

like image 158
transang Avatar answered Nov 14 '22 22:11

transang