【问题标题】:Why are variable values in closures getting lost after repeatedly calling lapply?为什么重复调用 lapply 后闭包中的变量值会丢失?
【发布时间】:2012-04-18 15:42:21
【问题描述】:

我正在尝试使用一系列lapply 调用来构建柯里化函数列表,理想情况下,在最后一次lapply 调用中,它会返回最终所需的值。柯里化有效,但lapply 似乎总是在第二次应用之后应用列表中的最后一个元素。

例子:

curry <- function(fn, ...) {
  arglist <- list(...)
  function(...) {
    do.call(fn, append(arglist, list(...)))
  }
}
# rcurry is used only to init the first lapply.
rcurry <- function(v1, fn, ...) {
  arglist <- append(list(v1), list(...))
  function(...) {
    do.call(fn, append(arglist, list(...)))
  }
}

myadd <- function(a,b,c) {
  a+b+c
}

这按预期工作:

# you can achieve the same by closure:
# curry.a <- lapply(c(10, 1000), FUN = function(a) { curry(myadd, a) })
curry.a <- lapply(list(10, 1000), rcurry, myadd)
curry.a[[1]](1,2)
curry.a[[2]](1,2)

# > [1] 13
# > [1] 1003

curry 中的下一个 lapply“破坏范围”:

# this does give the desired output:
# curry.a.b <- list(curry(curry.a[[1]], 1), curry(curry.a[[2]], 1))
curry.a.b <- lapply(curry.a, curry, 1)
curry.a.b[[1]](2)
curry.a.b[[2]](2)

# > [1] 1003
# > [1] 1003

这似乎不是 curry 或 rcurry 函数的结果。使用roxygen 的Curry 函数做同样的事情。通过上面的闭包或使用curry.a &lt;- list(curry(myadd, 10), curry(myadd, 1000)) 创建curry.a 也会产生相同的结果。

当然还有最后的咖喱:

# it doesn't work if you re-define this:
# curry.a.b <- list(curry(curry.a[[1]], 1), curry(curry.a[[2]], 2))
curry.a.b.c <- lapply(curry.a.b, curry, 2)
lapply(curry.a.b.c, do.call, list())

# > [1] 1003
# > [1] 1003

这是怎么回事?

【问题讨论】:

  • lapply 在新环境中评估 FUN。我认为这与它有关。
  • 我觉得答案和汤米给stackoverflow.com/questions/9950144/…的回复一样
  • Joshua 很暖和,kohske 很满意。谢谢大家。

标签: r scope closures currying lapply


【解决方案1】:

curry 中的fn 不在函数范围内评估,因此它是promise。 如果你force它,那么你可以得到你所期望的:

curry <- function(fn, ...) {
  force(fn)
  arglist <- list(...)
  function(...) {
    do.call(fn, append(arglist, list(...)))
  }
}

那么,

> curry.a.b <- lapply(curry.a, curry, 1)
> curry.a.b[[1]](2)
[1] 13
> curry.a.b[[2]](2)
[1] 1003
> 
> curry.a.b.c <- lapply(curry.a.b, curry, 2)
> lapply(curry.a.b.c, do.call, list())
[[1]]
[1] 13

[[2]]
[1] 1003

在内部,lapply 生成一个局部变量 X,每个函数调用都会引用该变量。如果在调用lapply 时没有在每个函数中评估X,则X 是承诺。调用lapply 后,X 在来自lapply 的所有函数调用中返回相同(即最后一个)值。所以lapply 类似于:

f0 <- function(i) function() i
f1 <- function(i) {force(i); function() i}

f <- local({
 r0 <- list()
 r1 <- list()
 for (i in 1:2) {
    r0[[i]] <- f0(i)
    r1[[i]] <- f1(i)
 }
 list(r0 = r0, r1 = r1)
})

那么,

> f$r0[[1]]()
[1] 2
> f$r1[[1]]()
[1] 1
> f$r0[[2]]()
[1] 2
> f$r1[[2]]()
[1] 2

【讨论】:

  • 100% 牛眼。谢谢!我实际上认为关闭是承诺,所以我做了closure &lt;- function(...) { do.call(fn, append(arglist, list(...)) }; force(closure); return closure。当然,这没有用。
猜你喜欢
  • 1970-01-01
  • 2014-11-26
  • 2018-02-05
  • 2014-09-20
  • 2014-08-23
  • 1970-01-01
  • 2023-03-21
  • 2018-03-19
  • 1970-01-01
相关资源
最近更新 更多