[R] returning functions inside lapply

Duncan Murdoch murdoch.duncan at gmail.com
Tue Apr 24 22:57:07 CEST 2012


On 12-04-24 4:22 PM, Ali Tofigh wrote:
> This has been asked before, but I just cannot figure out why lapply
> should behave this way given that R uses lazy evalution. Even after
> reading (or at least trying to read) parts of the R language
> definition.
>
>> f<- function(x) {function() {x}}
>> a<- list(f(1), f(2), f(3))
>> a[[1]]() # as expected
> [1] 1
>> a[[2]]() # as expected
> [1] 2
>> a[[3]]() # as expected
> [1] 3
>> b<- sapply(1:3, f)
>> b[[1]]() # why?
> [1] 3
>> b[[2]]() # why?
> [1] 3
>> b[[3]]() # expected, kind of...
> [1] 3
>
> Now I know that if I force the evaluation of x inside my function f,
> that the behaviour will change, i.e., b[[1]]() will return the value 1
> etc. But could some knowledgeable person please tell me how the lazy
> evaluation as implemented in R results in, what I preceive to be, a
> very strange behaviour when using lapply as above? I thought that
> lapply calls f three times and returns a list with whatever f
> returned. Is this not so?

That is so.  In each case, f creates a function that looks in its 
enclosing environment for the variable x to be returned.

That enclosing environment is the evaluation frame of f, where x is the 
argument being passed.

But x is never used in evaluating f, so the promise to evaluate x is 
never forced until you finally call one of those functions.

That means x will refer to some internal variable in lapply (which 
sapply calls).  You'll have 3 different promises to evaluate it, but 
they all evaluate to the same value.

You will get the result you want by forcing evaluation of x in f.  Then 
the three promises get evaluated before the internal value gets changed.

f<- function(x) { force(x); function() {x}}
b<- sapply(1:3, f)
b[[1]]()  # gives 1

You should almost always force evaluation of arguments before returning 
a constructed function to avoid problems like you had.

Duncan Murdoch



More information about the R-help mailing list