R

时间:2018-02-05 04:58:27

标签: r lazy-loading

我在R中有以下代码:

named_list = list()
for (i in 1:5){
named_list[[i]] = function(one,two){c(one,two, i)}
}

但是,当我调用该函数时:

> named_list[[1]]("first", "second")
[1] "first"  "second" "5"

有没有办法让它正常工作(在不使用应用功能的情况下返回"第一","第二"," 1")?我试图在另一个线程中使用强制函数,但我无法让它工作。

感谢。

编辑:为了澄清一下,我正在寻找一个函数列表,每个函数都包含该函数在该列表中的位置的索引。特别是,观察

> 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"

这显然不是理想的行为。问题是循环i到1到5,R看到第一个' i'索引named_list,但没有看到第二个' i'这是我试图定义的函数内部。

我知道以下是一个可能的解决方案(虽然我不知道为什么它有效):

named_list = lapply(1:5, function(i) function(one,two)(c(one,two,i)))

但我想知道是否有使用for循环的替代解决方案。

2 个答案:

答案 0 :(得分:2)

我认为您的问题与范围命名空间有关。也就是说,当在函数和变量中引用尚未在该函数中本地定义的函数时,R开始在父“frame”(定义其变量的环境)中进行搜索;如果没有,那么它会转到父母的父框架(祖父框架?);等等(一个很好的解读是Advanced R: Environments;额外的阅读可能是同一本书关于Memory的章节。)

查看在任何给定时间使用/搜索的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> 

首先请注意,每次迭代都在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

因此,当我们引用i时,R会按顺序搜索以下内容:

  • 8f811b8:在function(one,two)...内,找不到
  • 90bb578:直接父母env,在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"

首先要注意的是,在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

这是R首次需要查找 i,因此它首先在1b1a6720内搜索(function(one,two)内未找到) ,然后在R_GlobalEnv

那为什么会返回“2”?

因为iR_GlobalEnv的值在我们调用nl2[[2]]时是i循环中for的最后一个值。见:

rm(i)
for (i in 1:100) { } # no-op
i
# [1] 100

更有说服力的是,如果我们现在尝试调用该函数:

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

因此,在该函数中对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

(您可能会注意到,当您调用函数时,本地环境每次都会更改,而父级则不会。这是因为当您调用函数时,它会在函数调用开始时使用新环境启动。您“知道“并依赖于此,因为你假设在你的函数的开头,没有定义变量。这是正常的。”

答案 1 :(得分:1)

每当我遇到这样的情况时,我决定将其作为文本写出来并将其包装在eval语句中。像这样。

named_list = list()
for (i in 1:5){

  eval(parse(text = paste0("named_list[[i]] = function(one,two){c(one,two,", i, ")}")))

}

named_list[[1]]("first", "second")

现在我

> named_list[[1]]("first", "second")
[1] "first"  "second" "1" 

根据需要。

所以我所做的只是做我所知道的我想要在文本中串起来并让它以这种方式进行评估。

可能有更好的解决方案,但这将为您完成工作。