2011-12-09 18 views
16

下如我所料码不起作用:奇怪的莽撞行为:我错过了什么?

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 

谁能告诉我为什么?

注意:我找到了一个解决方法,所以我没有寻找一种不同的方法来达到理想的效果。但我很好奇,为什么这种特定的方法不起作用。


更新:

作为R 3.2.0,这现在可以按预期:

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 
+0

+1找到这种奇怪的行为!我喜欢这些东西:-) – Tommy

+0

可能不是一个适用的东西:'fs < - list();对于(我在1:2中)fs [[i]] < - f(a [[i]],b [[i]])'做同样的事情。 – pete

回答

8

[更新]我最初的分析是正确的,但得出的结论是错误的:)让我们在分析后得出结论。

下面是一些代码演示效果:

x <- lapply(1:3, function(x) sys.frame(sys.nframe())) 
x[[1]] # An environment 
x[[2]] # Another environment 
x[[3]] # Yet nother environment 
x[[1]]$x # 3!!! (should be 1) 
x[[2]]$x # 3!! (should be 2) 
x[[3]]$x # 3 as expected 

# Accessing the variable within the function will "fix" the weird behavior: 
x <- lapply(1:3, function(x) {x; sys.frame(sys.nframe())}) 
x[[1]]$x # 1 
x[[2]]$x # 2 
x[[3]]$x # 3 

所以工作围绕在你的情况:

f <- function(a, b) { a;b; function(x) a*x + b } 

顺便说一句,作为@詹姆斯指出存在force功能,可访问变量更明确:

f <- function(a, b) { force(a);force(b); function(x) a*x + b } 

Conclu sions

那么,@mbq和@hadley指出,这是由于懒惰的评价。它更容易与展示一个简单的for循环:

fs <- list(); for(i in 1:2) fs[[i]] <- f(a[[i]], b[[i]]) 

功能fx参数将得到价值a[[i]](这是0),但整个表达ai存在的环境。当您访问x时,会进行评估,因此在评估时使用i。如果自从调用f以来for循环已经开始,您将得到“错误”结果...

最初我说这是由于*apply中的一个错误,事实并非如此。 ......但因为我恨是错的,我可以指出,*适用确实有在这些情况下的错误(或者更不一致的):

lapply(11:12, function(x) sys.call()) 
#[[1]] 
#FUN(11:12[[1L]], ...) 
# 
#[[2]] 
#FUN(11:12[[2L]], ...) 

lapply(11:12, function(x) function() x)[[1]]() # 12 
lapply(11:12, function(x) function() x)[[2]]() # 12 

正如你看到的上面的lapply代码它调用与11:12[[1L]]功能。如果您评估“稍后”应该仍然得到值11 - 但你实际上得到12

这是可能由于事实lapply是用C语言实现的性能,而且欺骗了一下,所以它显示的表达是不是被评估的表达 - ERGO,一个bug ......

QED

+0

请参阅'?force'以获得类似的例子 – James

+4

这不是一个错误。这是懒惰评估的结果。 – hadley

+0

@hadley - 我站好了。我已经更新了我的答案。 – Tommy

9

这是懒惰的评价结果​​ - 所有的参数都是传下来的调用树为承诺,以避免不必要的执行,并保持在此暂停状态,直到R是确信他们使用。

在你的代码中,你只需要对与b相同的承诺填充函数;然后他们都承诺最后一对价值。正如@Tommy已经展示的那样,解决方案是在函数被定义之前通过“使用”值强制承诺。

相关问题