amit amit - 2 months ago 11
R Question

returning functions in R - when does the binding occur?

As in other functional languages, returning a function is a common case in R. for example, after training a model you'd like to return a "predictor" object, which is essentially a function, that given new data, returns predictions. There are other cases when this is useful, of course.

My question is when does the binding (e.g. evaluation) of values within the returned function occur.

As a simple example, suppose I want to have a list of three functions, each is slightly different based on a parameter whose value I set at the time of the creation of the function. Here is a simple code for this:

function.list = list()
for (i in 1:3) function.list[[i]] = function(x) x+i


So now I have three functions. Ideally, the first one returns x+1, the second computes x+2 and the third computes x+3

so I would expect:

function.list[[1]] (3) = 4
function.list[[2]] (3) = 5


etc.

Unfortunately, this doesn't happen and all the functions in the list above compute the same x+3. my question is why? why does the binding of the value of i is so late, and hence the same for all the functions in the list? How can I work around this?

EDIT:
rawr's link to a similar question was insightful, and I thought it solved the problem. Here is the link:
Explain a lazy evaluation quirk

however, I checked the code I gave above, with the fix suggested there, and it still doesn't work. Certainly, I miss something very basic here. can anyone tell me what is it? here is the "fixed" code (that still doesn't work)

function.list = list()
for (i in 1:3) { force(i); function.list[[i]] = function(x) x+i}


Still function.list[[1]] (3) gives 6 and not 4 as expected.
I also tried the following (e.g. putting the force() inside the function)

function.list = list()
for (i in 1:3) function.list[[i]] = function(x) {force(i);x+i}


what's going on?

Answer

Here's a solution with a for loop, using R 3.1:

> makeadd=function(i){force(i);function(x){x+i}}
> for (i in 1:3) { function.list[[i]] = makeadd(i)}
> rm(i) # not necessary but causes errors if we're actually using that `i`
> function.list[[1]](1)
[1] 2
> function.list[[2]](1)
[1] 3

The makeadd function creates the adding function in a context with a local i, which is why this works. It would be interesting to know if this works without the force in R 3.2. I always use the force, Luke....

Comments