R中的封闭环境,功能环境等不同

jua*_*nli 6 environment r function

我对函数的不同环境有几个问题.请看以下示例:

environment(sd)
# <environment: namespace:stats>
Run Code Online (Sandbox Code Playgroud)

命名空间:stats是否指向函数sd的封闭环境?

pryr::where(sd) 
# <environment: package:stats>
Run Code Online (Sandbox Code Playgroud)

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

根据Hadley Wickham的高级R说:"封闭的环境属于这个功能,永远不会改变......"

但是功能的封闭环境可以改变如下:

new.env <- new.env()
environment(f) <- new.env
Run Code Online (Sandbox Code Playgroud)

函数'environment属性表示函数的执行环境,对吗?关于R通过环境查找内容的在线文章

总结一下我的问题:

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

它类似于一前一后在这里.

Jor*_*eys 15

TLDR:

  1. 实际上,您可以更改封闭环境.哈德利可能正在谈论打包的功能.
  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>
Run Code Online (Sandbox Code Playgroud)

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

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

> f <- sd
> environment(f)
<environment: namespace:stats>
> find("f")
[1] ".GlobalEnv"
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?封闭的环境仍然是名称空间''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)
Run Code Online (Sandbox Code Playgroud)

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

但由于e是一个环境,R称之为父环境.

> parent.env(e)
<environment: R_GlobalEnv>
> f(1:3)
[1] 1 
Run Code Online (Sandbox Code Playgroud)

命名空间和包环境

这个原则也是"技巧"包使用:

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

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

  • 函数必须能够找到其他函数(对象),因此本地环境必须包含它导入的其他包的名称空间,基本包以及最后的全局环境.
  • 必须可以从全局环境中找到函数.因此,绑定(即函数的名称)必须位于由全局环境包围的环境中.这是包环境(不是命名空间!)

举例说明:

在此输入图像描述

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

> 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"
Run Code Online (Sandbox Code Playgroud)

父框架

这是它变得有趣的地方.父框架或调用环境是查找作为参数传递的值的环境.但是父框架可以是另一个功能的本地环境.在这种情况下,R首先查看该另一个函数的本地环境,然后在调用函数的封闭环境中,一直到全局环境,附加包的环境,直到它到达空环境.这就是"找不到对象"错误的地方.