奇怪的mapply行为:我错过了什么?

pet*_*ete 16 r

以下代码无法正常工作:

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)

mbq*_*mbq 9

这是延迟评估的结果 - 所有参数都在调用树下传递,以避免不必要的执行并保持在此挂起状态,直到R确信它们已被使用.

在您的代码中,您只需使用与b相同的承诺填充函数; 然后他们都致力于最后一对价格.正如@Tommy已经表明的那样,解决方案是通过在定义函数之前"使用"值来强制承诺.


Tom*_*mmy 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
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)

该函数fx参数将不会获得价值a[[i]](这是0),但整体表现和所在环境ai存在.当您访问时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

  • 这不是一个错误.这是懒惰评估的结果. (4认同)