The following code does not work as I expected:
a <- list(0, 1)
b <- list(0, 1)
# return a linear function with slope `a` and intercept `b`.
f <- function(a, b) function(x) a*x + b
# create a list of functions with different parameters.
fs <- mapply(f, a, b)
# test
fs[[1]](3)
# [1] 4 # expected zero!
fs[[2]](3)
# [1] 4
Can anyone tell me why?
NB: I’ve found a workaround, so I’m not looking for a different way to achieve the desired result. But I’m curious as to why this particular approach didn’t work.
Update:
As of R 3.2.0, this now works as expected:
a <- list(0, 1)
b <- list(0, 1)
f <- function(a, b) function(x) a*x + b
fs <- mapply(f, a, b)
# test
fs[[1]](3)
# [1] 0
fs[[2]](3)
# [1] 4
[Update] My initial analysis was correct but the conclusions were wrong 🙂 Let’s get to the conclusions after the analysis.
Here’s some code demonstrating the effects:
So the work-around in your case:
Btw, as @James notes there is a
forcefunction that makes accessing a variable more explicit:Conclusions
Well, as @mbq and @hadley noted, this is due to lazy evaluation. It’ easier to show with a simple for-loop:
The function
f‘sxargument will not get the value ofa[[i]](which is0), but the whole expression and the environment whereaandiexist. When you accessx, it gets evaluated and therefore uses theiat the time of evaluation. If the for-loop has moved on since the call tof, you get the “wrong” result…Initially I said that this was due to a bug in
*apply, which it isn’t. …but since I hate to be wrong, I can point out that *apply DOES have a bug (or perhaps more of an inconsistency) in these cases:As you see above, the
lapplycode says it calls the function with11:12[[1L]]. If you evaluate that “later” you should still get the value11– but you actually get12!This is probably due to the fact that
lapplyis implemented in C code for performance reasons and cheat a bit, so the expression that it shows is not the expression that gets evaluated – ergo, a bug…QED