R 中的列表中嵌入的闭包是否存在错误?
Is there a bug on closures embedded in a list in R?
这是一个闭包的简单示例,它是一个函数 returning 一个带有嵌入数据的函数(在 http://adv-r.had.co.nz/Functional-programming.html#closures 之后):
fFactory <- function(letter) {
function(Param) {
paste("Enclosed variable:", letter, "/ function parameter:", Param)
}
}
创建函数时,letter
用于returned函数:
> FUN <- fFactory("a")
> FUN("toto")
[1] "Enclosed variable: a / function parameter: toto"
之所以有效,是因为变量 letter
嵌入在函数的环境中:
as.list(environment(FUN))
$letter
[1] "a"
如果现在我们在这样的列表中创建函数:
l <- list()
for(letter in letters) {
l[[letter]]$FUN <- fFactory(letter)
}
通常,运行项目“a”的函数必须return与以前相同的结果,但事实并非如此:
> l[["a"]]$FUN("toto")
[1] "Enclosed variable: z / function parameter: toto"
很明显是因为函数嵌入的环境不是我们预期的:
> as.list(environment(l[["a"]]$FUN))
$letter
[1] "z"
它return是在列表的最后一项为列表中的所有闭包创建的最后一个闭包。
我想我这样做并没有滥用 R 语言,而是语言中存在错误。你们中的任何人都可以确认或解释我的错误在哪里?
用 force()
.
强制计算参数 letter
fFactory2 <- function(letter) {
force(letter)
function(Param) {
paste("Enclosed variable:", letter, "/ function parameter:", Param)
}
}
l2 <- list()
for(letter in letters) {
l2[[letter]]$FUN <- fFactory2(letter)
}
l2[["a"]]$FUN("toto")
l2[["b"]]$FUN("toto")
l2[["w"]]$FUN("toto")
这里有一个解释(在@user2554330 之后):
In R, arguments to functions aren't evaluated until first used. So the arguments to all of the functions in your list are the global variable letter, which you change in the loop as you create them, but you never evaluate until you call them. So the functions first evaluate letter at the time of the first call, and you get strange results.
这是你的错误。 @RuiBarradas 为您提供解决方案。这里有一个解释:
在 R 中,函数的参数在第一次使用之前不会被评估。因此,列表中所有函数的参数都是全局变量 letter
,您在创建它们时在循环中对其进行更改,但在调用它们之前永远不会计算。所以函数在第一次调用时首先计算 letter
,你会得到奇怪的结果。
您可以按照 Rui 所说的方式解决此问题:在创建函数之前强制对参数求值。
这是一个闭包的简单示例,它是一个函数 returning 一个带有嵌入数据的函数(在 http://adv-r.had.co.nz/Functional-programming.html#closures 之后):
fFactory <- function(letter) {
function(Param) {
paste("Enclosed variable:", letter, "/ function parameter:", Param)
}
}
创建函数时,letter
用于returned函数:
> FUN <- fFactory("a")
> FUN("toto")
[1] "Enclosed variable: a / function parameter: toto"
之所以有效,是因为变量 letter
嵌入在函数的环境中:
as.list(environment(FUN))
$letter
[1] "a"
如果现在我们在这样的列表中创建函数:
l <- list()
for(letter in letters) {
l[[letter]]$FUN <- fFactory(letter)
}
通常,运行项目“a”的函数必须return与以前相同的结果,但事实并非如此:
> l[["a"]]$FUN("toto")
[1] "Enclosed variable: z / function parameter: toto"
很明显是因为函数嵌入的环境不是我们预期的:
> as.list(environment(l[["a"]]$FUN))
$letter
[1] "z"
它return是在列表的最后一项为列表中的所有闭包创建的最后一个闭包。
我想我这样做并没有滥用 R 语言,而是语言中存在错误。你们中的任何人都可以确认或解释我的错误在哪里?
用 force()
.
letter
fFactory2 <- function(letter) {
force(letter)
function(Param) {
paste("Enclosed variable:", letter, "/ function parameter:", Param)
}
}
l2 <- list()
for(letter in letters) {
l2[[letter]]$FUN <- fFactory2(letter)
}
l2[["a"]]$FUN("toto")
l2[["b"]]$FUN("toto")
l2[["w"]]$FUN("toto")
这里有一个解释(在@user2554330
In R, arguments to functions aren't evaluated until first used. So the arguments to all of the functions in your list are the global variable letter, which you change in the loop as you create them, but you never evaluate until you call them. So the functions first evaluate letter at the time of the first call, and you get strange results.
这是你的错误。 @RuiBarradas 为您提供解决方案。这里有一个解释:
在 R 中,函数的参数在第一次使用之前不会被评估。因此,列表中所有函数的参数都是全局变量 letter
,您在创建它们时在循环中对其进行更改,但在调用它们之前永远不会计算。所以函数在第一次调用时首先计算 letter
,你会得到奇怪的结果。
您可以按照 Rui 所说的方式解决此问题:在创建函数之前强制对参数求值。