14

When trying to create a list of similar functions using lapply, I find that all the functions in the list are identical and equal to what the final element should be.

Consider the following:

pow <- function(x,y) x^y
pl <- lapply(1:3,function(y) function(x) pow(x,y))
pl
[[1]]
function (x) 
pow(x, y)
<environment: 0x09ccd5f8>

[[2]]
function (x) 
pow(x, y)
<environment: 0x09ccd6bc>

[[3]]
function (x) 
pow(x, y)
<environment: 0x09ccd780>

When you try to evaluate these functions you get identical results:

pl[[1]](2)
[1] 8
pl[[2]](2)
[1] 8
pl[[3]](2)
[1] 8

What is going on here, and how can I get the result I desire (the correct functions in the list)?

Eike P.
  • 3,333
  • 1
  • 27
  • 38
James
  • 65,548
  • 14
  • 155
  • 193
  • I am not sure, what your goal is. Maybe `pl <- function(x,y) lapply(y,function(y) pow(x,y)); pl(2,1:3)`? – Roland Apr 01 '13 at 11:14
  • These notes by Ross Ihaka (RCore) might be helpful (specifically the part about Lazy Evaluation) www.stat.auckland.ac.nz/~ihaka/downloads/Waikato-WRUG.pdf – Ricardo Saporta Apr 01 '13 at 14:29
  • Note that this is no longer true as of R 3.2.0, see my answer below. – Eike P. Apr 29 '15 at 00:31

2 Answers2

20

R passes promises, not the values themselves. The promise is forced when it is first evaluated, not when it is passed, and by that time the index has changed if one uses the code in the question. The code can be written as follows to force the promise at the time the outer anonymous function is called and to make it clear to the reader:

pl <- lapply(1:3, function(y) { force(y); function(x) pow(x,y) } )
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • Thanks, it's good to know how this pitfall works. I'll have to keep this in mind in the future. – James Apr 02 '13 at 07:29
8

This is no longer true as of R 3.2.0!

The corresponding line in the change log reads:

Higher order functions such as the apply functions and Reduce() now force arguments to the functions they apply in order to eliminate undesirable interactions between lazy evaluation and variable capture in closures.

And indeed:

pow <- function(x,y) x^y
pl <- lapply(1:3,function(y) function(x) pow(x,y))
pl[[1]](2)
# [1] 2
pl[[2]](2)
# [1] 4
pl[[3]](2)
# [1] 8
Eike P.
  • 3,333
  • 1
  • 27
  • 38