以下代码无法正常工作:
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
Run Code Online (Sandbox Code Playgroud)
谁能告诉我为什么?
注意:我找到了一个解决方法,所以我不是在寻找一种不同的方法来达到预期的效果.但我很好奇为什么这种特殊方法不起作用.
更新:
从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
Run Code Online (Sandbox Code Playgroud)
这是延迟评估的结果 - 所有参数都在调用树下传递,以避免不必要的执行并保持在此挂起状态,直到R确信它们已被使用.
在您的代码中,您只需使用与b相同的承诺填充函数; 然后他们都致力于最后一对价格.正如@Tommy已经表明的那样,解决方案是通过在定义函数之前"使用"值来强制承诺.
[更新]我的初步分析是正确的,但结论是错误的:)让我们在分析后得出结论.
这是一些展示效果的代码:
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
Run Code Online (Sandbox Code Playgroud)
所以在你的情况下解决:
f <- function(a, b) { a;b; function(x) a*x + b }
Run Code Online (Sandbox Code Playgroud)
顺便说一句,正如@James所说,有一个force
函数可以更明确地访问变量:
f <- function(a, b) { force(a);force(b); function(x) a*x + b }
Run Code Online (Sandbox Code Playgroud)
结论
好吧,正如@mbq和@hadley所说,这是由于懒惰的评估.使用简单的for循环更容易显示:
fs <- list(); for(i in 1:2) fs[[i]] <- f(a[[i]], b[[i]])
Run Code Online (Sandbox Code Playgroud)
该函数f
的x
参数将不会获得价值的a[[i]]
(这是0
),但整体表现和所在环境a
和i
存在.当您访问时x
,它会被评估,因此i
在评估时使用.如果for-loop自调用之后已经移动f
,则会得到"错误"结果...
最初我说这是由于一个错误*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
Run Code Online (Sandbox Code Playgroud)
如上所示,lapply
代码表示它调用了函数11:12[[1L]]
.如果你评价"以后"你仍然应该得到价值11
- 但你真的得到了12
!
这可能是由于这一事实lapply
是用C语言实现的性能,而且欺骗了一下,所以它显示的表达是不是被评估的表达- ERGO,一个bug ...
QED