以下代码无法正常工作:
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 :(得分:9)
这是延迟评估的结果 - 所有参数都在调用树下传递,以避免不必要的执行并保持在此挂起状态,直到R确信它们已被使用。
在您的代码中,您只需使用与b相同的承诺填充函数;然后他们都致力于最后一对价格。正如@Tommy已经表明的那样,解决方案是通过在定义函数之前“使用”值来强制承诺。
答案 1 :(得分: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 }
顺便说一下,正如@James所说,有一个force
函数可以更明确地访问变量:
f <- function(a, b) { force(a);force(b); function(x) a*x + b }
<强>结论强>
好吧,正如@mbq和@hadley所说,这是由于懒惰的评估。使用简单的for循环更容易显示:
fs <- list(); for(i in 1:2) fs[[i]] <- f(a[[i]], b[[i]])
函数f
的{{1}}参数不获取x
的值(a[[i]]
}),但整个表达式以及0
和a
存在的环境。当您访问i
时,会对其进行评估,因此在评估时会使用x
。如果从调用i
开始,for循环已经移动,则会得到“错误”的结果......
最初我说这是由于f
中的一个错误,但事实并非如此。 ...但由于我讨厌错误,我可以指出* apply DOES在这些情况下有一个错误(或者更多的不一致):
*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
由于性能原因而在C代码中实现并且有点作弊,所以它显示的表达式不是得到评估的表达式 - ergo,一个bug ......
QED