R中截然不同的封闭环境、函数环境等

Distinct enclosing environment, function environment, etc. in R

我有几个关于函数的不同环境的问题。举个例子:

environment(sd)
# <environment: namespace:stats>

namespace:stats是否指向函数sd的封闭环境?

pryr::where(sd) 
# <environment: package:stats>

package:stats是否指向函数sd的绑定环境?

根据Advanced R by Hadley Wickham:"The enclosing environment belongs to the function, and never changes..."

但是函数的封装环境可以像下面这样改变:

new.env <- new.env()
environment(f) <- new.env

A function' environment 属性表示函数的执行环境,对吗? An online article regarding R finding stuff through environments

总结一下我的问题:

  1. 我们真的可以改变函数的封闭环境吗?
  2. stats 包的这两个不同环境是什么?
  3. 函数的环境是什么?

它与之前的 post in here 相似。

environment(function) 给出了函数的封闭环境(即闭包),它被分配了一个指向定义函数的环境的指针。这种约定称为词法作用域,可以让您使用工厂函数等模式。这是一个简单的例子

factory <- function(){
    # get a reference to the current environment -- i.e. the environment 
    # that was created when the function `factory` was called.
    envir = environment()
    data <- 0
    add <- function(x=1){
        # we can use the lexical scoping assignment operator to re-assign the value of data
        data <<- data + x
        # return the value of the lexically scoped variable `data`
        return(data)
    }
    return(list(envir=envir,add=add))
}

L = factory()

# check that the environment for L$add is the environment in which it was created
identical(L$envir,environment(L$add))
#> TRUE

L$add()
#> 1
L$add(3)
#> 4

请注意,我们可以使用 assign() 在封闭环境中重新分配 data 的值,如下所示:

assign("data",100,L$envir)
L$add()
#> 101

另外,当我们再次调用函数factory()时,又创建了一个新的环境 并被指定为在其中定义的函数的闭包 函数调用,这使我们必须分离 foo$add() 函数 他们自己独立环境的范围:

M = factory()
M$add()
#> 1
#> 2
L$add()
#> 102

上面的工厂函数通过继续搜索变量(并使用作用域赋值运算符)说明了函数与其封闭环境之间的 link,而下面说明了 link在本地环境和调用框架之间通过 Promises 这就是 R 在函数调用中传递变量的方式。

具体来说,当您调用函数时,R 会为传递的变量和表达式的值创建承诺。 Promise 的这些值是通过在参数被 force() 或使用时在调用环境的上下文中评估 Promise 从变量/表达式传递(复制)的——而不是更早!

例如,这个工厂函数接受一个参数,在返回的函数被调用之前,该参数被存储为一个承诺:

factory2 <- function(x){
    out <-function(){
         return(x)
    }
    return(out)
}

现在 factory2 在某些情况下表现直观:

y = 1
f = factory2(y)
f()
#> 1

但在其他人中没有:

y = 1
h = factory2(y)
y = 2
h()
#> 2

因为表达式 y 的 promise 在 h() 被调用之前不会被计算,而在第二个例子中,y 的值是 2!当然,既然已经通过 Promise 评估将值从调用环境复制到本地环境,更改 y 的值将不会影响 h():

返回的值
y = 3
h()
#> 2

TLDR:

  1. 确实,您可以更改封闭环境。 Hadley 可能在谈论打包函数。
  2. 封闭和绑定环境。你是对的。
  3. 这就是执行环境。它仅在函数运行时存在。

函数环境

谈一个功能要分清4种不同的环境:

  • 绑定环境 是找到函数的环境(即它的名称存在的地方)。这是一个对象与其名称的实际绑定完成的地方。 find() 为您提供绑定环境。
  • 封闭环境是最初创建函数的环境。这不一定与绑定环境相同(请参见下面的示例)。 environment() 给你封闭的环境。
  • 局部环境是函数内的环境。你称之为执行环境。
  • 父框架或调用环境是调用函数的环境。

这有什么关系

每个环境都有特定的功能:

  • 绑定环境是您找到函数的环境。
  • 局部环境是R寻找对象的第一个环境。
  • 一般规则是:如果 R 在本地环境中找不到对象,它就会在封闭环境中查找,依此类推。最后一个封闭环境总是 emptyenv().
  • 父框架是 R 查找传递的对象值的地方 参数。

您可以更改封闭环境

的确,您可以更改封闭环境。它是包 中函数 的封闭环境,您无法更改。在那种情况下,您不会更改封闭环境,您实际上会在新环境中创建一个副本:

> ls()
character(0)
> environment(sd)
<environment: namespace:stats>
> environment(sd) <- globalenv()
> environment(sd)
<environment: R_GlobalEnv>
> ls()
[1] "sd"
> find("sd")
[1] ".GlobalEnv"    "package:stats" # two functions sd now
> rm(sd)
> environment(sd)
<environment: namespace:stats>

在这种情况下,第二个sd具有全局环境作为封装和绑定环境,但原始sd仍然在包环境中找到,它的封装环境仍然是该包的命名空间

执行以下操作时可能会出现混淆:

> f <- sd
> environment(f)
<environment: namespace:stats>
> find("f")
[1] ".GlobalEnv"

这里发生了什么?封闭环境仍然是命名空间“'stats'”。这就是创建函数的地方。但是,绑定环境现在是全局环境。这就是名称 "f" 绑定到对象的地方。

我们可以将封闭环境更改为新环境e。如果现在检查,封闭环境变为 e,但 e 本身是空的。 f 仍然绑定在全局环境中。

> e <- new.env()
> e
<environment: 0x000000001852e0a8>
> environment(f) <- e
> find("f")
[1] ".GlobalEnv"
> environment(f)
<environment: 0x000000001852e0a8>
> ls(e)
character(0)

e的封闭环境是全局环境。所以 f 仍然像它的外壳是全局环境一样工作。环境 e 包含在其中,因此如果在 e 中找不到某些内容,该函数将在全局环境中查找,依此类推。

但是因为 e 是一个环境,R 称它为父环境。

> parent.env(e)
<environment: R_GlobalEnv>
> f(1:3)
[1] 1 

命名空间和包环境

这个原理也是"trick"包使用的:

  • 函数是在命名空间中创建的。这是一个被其他导入包的命名空间包围的环境,最终是全局环境。
  • 函数的绑定是在包环境中创建的。这是一个包含全局环境和可能的其他包的环境。

原因很简单:对象只能在您所在的环境中或其封闭环境中找到。

  • 一个函数必须能够找到其他函数(对象),因此局部环境必须包含在它导入的其他包的命名空间、基础包和最后的全局环境中。
  • 函数必须可以在全局环境中找到。因此,绑定(即函数的名称)必须位于被全局环境包围的环境中。这是包环境(不是命名空间!)

插图:

现在假设您创建一个以空环境作为父环境的环境。如果您将其用作函数的封闭环境,则不再起作用。因为现在你绕过了所有的包环境,所以你再也找不到一个函数了。

> orphan <- new.env(parent = emptyenv())
> environment(f) <- orphan
> f(1:3)
Error in sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x),  : 
  could not find function "sqrt"

父框架

这就是有趣的地方。父框架或调用环境是查找作为参数传递的值的环境。但是那个父框架可以是另一个函数的本地环境。在这种情况下,R 首先在另一个函数的局部环境中查找,然后在调用函数的 enclosing 环境中查找,一直到全局环境,附加的包,直到它到达空的环境。这就是 "object not found" 错误所在。