我在 R 中有以下代码:
named_list = list()
for (i in 1:5){
named_list[[i]] = function(one,two){c(one,two, i)}
}
Run Code Online (Sandbox Code Playgroud)
但是,当我调用该函数时:
> named_list[[1]]("first", "second")
[1] "first" "second" "5"
Run Code Online (Sandbox Code Playgroud)
有没有办法让它正常工作(返回“第一”,“第二”,“1”)而不使用应用函数?我尝试按照另一个线程中的建议使用force函数,但我无法让它工作。
谢谢。
编辑:为了澄清一些问题,我希望制作一个函数列表,每个函数都包含该函数在该列表中的位置的索引。特别要注意的是
> named_list[[1]]("first", "second")
[1] "first" "second" "5"
> named_list[[2]]("first", "second")
[1] "first" "second" "5"
> named_list[[3]]("first", "second")
[1] "first" "second" "5"
> named_list[[4]]("first", "second")
[1] "first" "second" "5"
> named_list[[5]]("first", "second")
[1] "first" "second" "5"
Run Code Online (Sandbox Code Playgroud)
这显然不是期望的行为。问题是,将 i 循环到 1 到 5,R 会看到第一个“i”索引named_list,但看不到我试图定义的函数内部的第二个“i”。
我知道以下是一个可能的解决方案(尽管我不知道它为什么有效):
named_list = lapply(1:5, function(i) function(one,two)(c(one,two,i)))
Run Code Online (Sandbox Code Playgroud)
但我想知道是否有使用 for 循环的替代解决方案。
我认为您的问题与范围或名称空间有关。也就是说,当在一个函数中引用了一个尚未在该函数中本地定义的变量时,R开始在父“框架”(定义其变量的环境)中搜索;如果不存在,则转到父级的父级框架(祖级父级框架?);(对此,一本不错的读物是《高级 R:环境》;额外的读物可能是同一本书中有关“内存”的章节。)
查看environment在任何给定时间的使用/搜索情况会很有帮助。我将重点关注当前环境、父环境以及在函数内部时的“祖父母”环境;不过,要意识到深度嵌套的函数可能有更多(这表明您在依赖 R 来寻找并查找不在本地环境中的变量的特定实例时需要非常小心!)。
注意:您很可能不会得到相同的<environment: 0x000...>指针。这些引用是完全不可重现的,并且每次运行此代码时都会发生变化。
让我们从有效的lapply设置开始:
print(environment())
# <environment: R_GlobalEnv>
nl1 <- lapply(1:2, function(i) {
e1 <- environment()
str(list(where="inside lapply", env=e1, parent=parent.env(e1)))
function(one,two) {
e2 <- environment()
str(list(where="inside func", env=e2, parent=parent.env(e2),
grandparent=parent.env(parent.env(e2))))
c(one, two, i)
}
})
# List of 3
# $ where : chr "inside lapply"
# $ env :<environment: 0x0000000009128fe0>
# $ parent:<environment: R_GlobalEnv>
# List of 3
# $ where : chr "inside lapply"
# $ env :<environment: 0x00000000090bb578>
# $ parent:<environment: R_GlobalEnv>
Run Code Online (Sandbox Code Playgroud)
首先请注意,在 中的每次迭代中lapply,都会有一个新环境,从 开始9128fe0,其父环境是全局环境。在 的第二次迭代中lapply,我们处于90bb578,并且在该环境中,我们定义了 的function(one,two)本地环境8f811b8(我们在下一个代码块中看到)。
意识到此时,R 尚未尝试解析i。让我们运行一个函数:
nl1[[2]](11,12)
# List of 4
# $ where : chr "inside func"
# $ env :<environment: 0x0000000008f811b8>
# $ parent :<environment: 0x00000000090bb578>
# $ grandparent:<environment: R_GlobalEnv>
# [1] 11 12 2
Run Code Online (Sandbox Code Playgroud)
因此,当我们引用 时i,R 会按顺序搜索以下内容来找到它:
8f811b8: 里面function(one,two)...没找到90bb578:直接父环境,内部function(i) ...;成立R_GlobalEnv(没有搜索,因为之前已经找到了)好的,让我们尝试一下for循环:
nl2 <- list()
for (i in 1:2) {
e1 <- environment()
str(list(where="inside for", env=e1, parent=parent.env(e1)))
nl2[[i]] <- function(one,two) {
e2 <- environment()
str(list(where="inside func", env=e2, parent=parent.env(e2),
grandparent=parent.env(parent.env(e2))))
c(one, two, i)
}
}
# List of 3
# $ where : chr "inside for"
# $ env :<environment: R_GlobalEnv>
# $ parent:<environment: package:tcltk>
# ..- attr(*, "name")= chr "package:tcltk"
# ..- attr(*, "path")= chr "c:/R/R-3.3.3/library/tcltk"
# List of 3
# $ where : chr "inside for"
# $ env :<environment: R_GlobalEnv>
# $ parent:<environment: package:tcltk>
# ..- attr(*, "name")= chr "package:tcltk"
# ..- attr(*, "path")= chr "c:/R/R-3.3.3/library/tcltk"
Run Code Online (Sandbox Code Playgroud)
首先要注意的是,在for循环的每次迭代中,本地环境是R_GlobalEnv,这应该是有意义的。tcltk(您可以安全地忽略对作为父级的环境的引用。)
好的,现在当我们接到电话时nl2[[1]],请注意父环境是(也许现在,并不奇怪)环境R_GlobalEnv:
nl2[[1]](11,12)
# List of 4
# $ where : chr "inside func"
# $ env :<environment: 0x000000001b1a6720>
# $ parent :<environment: R_GlobalEnv>
# $ grandparent:<environment: package:tcltk>
# ..- attr(*, "name")= chr "package:tcltk"
# ..- attr(*, "path")= chr "c:/R/R-3.3.3/library/tcltk"
# [1] 11 12 2
Run Code Online (Sandbox Code Playgroud)
这是 R 第一次需要查找 i,因此它首先在 内1b1a6720(在 内function(one,two),没有找到)进行搜索,然后在R_GlobalEnv.
那么为什么它返回“2”呢?
因为在我们调用 时,iin的值是循环中的最后一个值。看看这个:R_GlobalEnvnl2[[2]]ifor
rm(i)
for (i in 1:100) { } # no-op
i
# [1] 100
Run Code Online (Sandbox Code Playgroud)
更能说明问题的是,如果我们现在尝试调用该函数:
nl2[[1]](11,12)
# List of 4
# $ where : chr "inside func"
# $ env :<environment: 0x000000000712c2a0>
# $ parent :<environment: R_GlobalEnv>
# $ grandparent:<environment: package:tcltk>
# ..- attr(*, "name")= chr "package:tcltk"
# ..- attr(*, "path")= chr "c:/R/R-3.3.3/library/tcltk"
# [1] 11 12 100
Run Code Online (Sandbox Code Playgroud)
因此,该函数内的评估i是惰性的,因为它在调用该函数时进行搜索。
在您的环境中(在更改任何代码之前),如果您输入i <- 100,您会看到类似的行为。
如果您绝对反对使用lapply(这是我在这里的首选方法,即使我不理解您的潜在需求),请尝试显式定义函数周围的环境。一种方法是使用local,它将保留在现有父环境中的搜索,同时允许我们“强制”i我们想要使用的内容。(存在其他选项,我邀请其他人发表评论,并让您更多地探索环境。)
nl3 <- list()
for (i in 1:2) {
e1 <- environment()
str(list(where="inside for", env=e1, parent=parent.env(e1)))
nl3[[i]] <- local({
i <- i # forces it locally within this env
function(one,two) {
e2 <- environment()
str(list(where="inside func", env=e2, parent=parent.env(e2),
grandparent=parent.env(parent.env(e2))))
c(one, two, i)
}
})
}
# List of 3
# $ where : chr "inside for"
# $ env :<environment: R_GlobalEnv>
# $ parent:<environment: package:tcltk>
# ..- attr(*, "name")= chr "package:tcltk"
# ..- attr(*, "path")= chr "c:/R/R-3.3.3/library/tcltk"
# List of 3
# $ where : chr "inside for"
# $ env :<environment: R_GlobalEnv>
# $ parent:<environment: package:tcltk>
# ..- attr(*, "name")= chr "package:tcltk"
# ..- attr(*, "path")= chr "c:/R/R-3.3.3/library/tcltk"
nl3[[1]](11,12)
# List of 4
# $ where : chr "inside func"
# $ env :<environment: 0x0000000019ca23e0>
# $ parent :<environment: 0x000000001aabe388>
# $ grandparent:<environment: R_GlobalEnv>
# [1] 11 12 1
i <- 1000
nl3[[1]](11,12)
# List of 4
# $ where : chr "inside func"
# $ env :<environment: 0x0000000008d0bc78>
# $ parent :<environment: 0x000000001aabe388>
# $ grandparent:<environment: R_GlobalEnv>
# [1] 11 12 1
Run Code Online (Sandbox Code Playgroud)
(您可能会注意到,每次调用函数时,本地环境都会发生变化,而父函数则不会。这是因为当您调用函数时,它会在函数调用开始时以新环境开始。您“知道”并且依赖于此,因为您假设在函数的开头没有定义任何变量。这是正常的。)