Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R anonymous function: capture variables by value

I've defined a list of anonymous functions which use a variable defined in an outer scope.

funclist <- list()
for(i in 1:5)
{
  funclist[[i]] <- function(x) print(i)
}

funclist[[1]]('foo')

The output is:

[1] 5

It seems that i is captured by reference. I'd like it to be captured by value, i.e. the output should be

[1] 1

Is there a way to tell R to capture i by value rather than by reference?

like image 490
user23797 Avatar asked Apr 25 '13 14:04

user23797


2 Answers

When you run a for loop, this creates a variable in the environment the loop is run in, and functions created in the loop are also run from this environment. So whenever you run the functions created in this way that use the index value from the loop, they only have access to the final value, and only as long as that varaible remains (try rm(i) and attempting to fun one of the functions in the list).

What you need to do is bind the index value to the function in their own environment. lapply will do this for you automatically. However, there is a gotcha with lazy evaluation. What you have to do is also force the evaluation of i before creating the anonymous function:

funclist <- lapply(1:5, function(i) {force(i); function(x) print(i)})
funclist[[1]]('foo')
[1] 1
funclist[[5]]('foo')
[1] 5
like image 68
James Avatar answered Sep 21 '22 00:09

James


My read on what you want is to store a value inside the function's environment when the function is defined, and then hold onto that value for internal computations.

For that, you want a closure:

i <- 3
test <- local({
  i <- i
  function(x) x[i]
})
test(letters[1:5]) # returns 'c'
i <- 5
test(letters[1:5]) # still returns 'c' (i.e. i is local to the test closure)

Is that what you wanted?

like image 29
Ari B. Friedman Avatar answered Sep 21 '22 00:09

Ari B. Friedman