在R中访问闭包中的变量

时间:2017-06-29 10:44:41

标签: r closures

在以下示例中,为什么f$if$get_i()会返回不同的结果?

factory <- function() {

  my_list <- list()
  my_list$i <- 1

  my_list$increment <- function() {
    my_list$i <<- my_list$i + 1
  }

  my_list$get_i <- function() {
    my_list$i
  }

  my_list
}

f <- factory()

f$increment()
f$get_i() # returns 2
f$i # returns 1

3 个答案:

答案 0 :(得分:6)

f <- factory()

使用my_list创建my_list$i = 1个对象,并将其分配给f。现在f$i = 1

f$increment() 

仅增加my_list$i。它不会影响f

现在

f$get_i() 

时返回(先前递增的)my_list$i
f$i 

返回不受影响的f$i

它&#39;因为您使用了对全局对象进行操作的<<-运算符。如果您将代码更改为

my_list$increment <- function(inverse) {
    my_list$i <- my_list$i + 1
}

my_list仅在increment函数内增加。所以现在你得到了

> f$get_i() 
[1] 1
> f$i 
[1] 1

让我在您的代码中添加一行,以便我们看到increment的肠子:

 my_list$increment <- function(inverse) {
    my_list$i <- my_list$i + 1
    return(my_list$i)
  }

现在,您可以看到<-仅在increment内部运作,<<-在其外运行。

> f <- factory()
> f$increment()
[1] 2
> f$get_i() 
[1] 1
> f$i
[1] 1

答案 1 :(得分:4)

根据@Cath关于“参考价值”的评论,我受到了启发,想出了这个。

library(data.table)
factory <- function() {
   my_list <- list()
   my_list$i <- data.table(1)

   my_list$increment <- function(inverse) {
     my_list$i[ j = V1:=V1+1]
  }

   my_list$get_i <- function() {
     my_list$i
   }
   my_list
 }
f <- factory()
f$increment()
f$get_i() # returns 2
   V1
1:  2
f$i # returns 1
   V1
1:  2
f$increment()
f$get_i() # returns 2
   V1
1:  3
f$i # returns 1
   V1
1:  3

答案 2 :(得分:4)

您编码的方式与功能范例非常相似。 R更常用作脚本语言。因此,除非您确切知道自己在做什么,否则使用&lt;&lt; - 或在函数中包含函数是不好的做法。

您可以在功能环境章节中找到解释here

环境是执行代码的空间/框架。环境可以嵌套,就像函数一样。

创建功能时,您附加了一个可由environment调用的机箱环境。这是封闭的环境。

该功能在另一个环境中执行,执行环境采用全新启动原则。执行环境是封闭环境的子环境。

例如,在我的笔记本电脑上:

> environment()
<environment: R_GlobalEnv>
> environment(f$increment)
<environment: 0x0000000022365d58>
> environment(f$get_i)
<environment: 0x0000000022365d58>

f是位于全球环境中的对象。

函数increment附加了封闭环境0x0000000022365d58,函数factory的执行环境。

我引用哈德利的话:

  

当你在另一个函数内创建一个函数时,封闭   子函数的环境是执行环境   父,并且执行环境不再是短暂的。

执行函数f时,将创建封闭环境,其中包含my_list对象。

可以使用ls命令评估:

> ls(envir = environment(f$increment))
[1] "my_list"
> ls(envir = environment(f$get_i))
[1] "my_list"

<<-运算符正在父项环境中搜索所使用的变量。在这种情况下,找到的my_list对象是直接上层环境中的对象,它是函数的封闭环境。

因此,当进行增量时,它仅在该环境中进行,而不是在全局中进行。

您可以通过替换increment函数来看到它:

  my_list$increment <- function() {
    print("environment")
    print(environment())
    print("Parent environment")
    print(parent.env(environment()))
    my_list$i <<- my_list$i + 1
  }

它给了我:

> f$increment()
[1] "environment"
<environment: 0x0000000013c18538>
[1] "Parent environment"
<environment: 0x0000000022365d58>

存储环境名称后,您可以使用get来访问您的结果:

> my_main_env <- environment(f$increment)
> get("my_list", env = my_main_env)
$i
[1] 2

$increment
function () 
{
    print("environment")
    print(environment())
    print("Parent environment")
    print(parent.env(environment()))
    my_list$i <<- my_list$i + 1
}
<environment: 0x0000000022365d58>

$get_i
function () 
{
    print("environment")
    print(environment())
    print("Parent environment")
    print(parent.env(environment()))
    my_list$i
}
<environment: 0x0000000022365d58>