首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >R中独特的封闭环境、功能环境等

R中独特的封闭环境、功能环境等
EN

Stack Overflow用户
提问于 2017-06-08 06:01:37
回答 2查看 1.5K关注 0票数 7

关于函数的不同环境,我有几个问题。以下列例子为例:

代码语言:javascript
复制
environment(sd)
# <environment: namespace:stats>

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

代码语言:javascript
复制
pryr::where(sd) 
# <environment: package:stats>

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

根据高级R,哈德利韦翰的说法:“封闭环境属于函数,永远不会改变.”

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

代码语言:javascript
复制
new.env <- new.env()
environment(f) <- new.env

函数的环境属性指示函数的执行环境,对吗?一篇关于R在环境中发现东西的在线文章

总结我的问题:

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

它类似于之前的一篇文章在这里

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-06-08 17:13:20

TLDR:

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

功能环境

在讨论函数时,您必须区分4种不同的环境:

  • 绑定环境是查找函数的环境(即它的名称存在的地方)。这是完成对象与其名称的实际绑定的地方。find()为您提供绑定环境。
  • 封装环境是最初创建函数的环境。这不一定与绑定环境相同(参见下面的示例)。environment()为您提供了封闭的环境。
  • 本地环境是函数中的环境。你把这叫做执行环境。
  • 父框架或调用环境是调用函数的环境。

,为什么这很重要,

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

  • 绑定环境是找到函数的环境。
  • 本地环境是R查找对象的第一个环境。
  • 一般的规则是:如果R没有在本地环境中找到一个对象,那么它就在封闭的环境中查找,等等。最后一个封闭环境总是emptyenv()
  • 父框架是R查找作为参数传递的对象的值的地方。

您可以更改封闭环境

实际上,您可以改变封闭的环境。它是包中函数的封闭环境,您不能更改它。在这种情况下,您不需要更改封闭环境,而是在新环境中创建一个副本:

代码语言:javascript
复制
> 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仍然存在于包环境中,其封闭环境仍然是该包的命名空间。

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

代码语言:javascript
复制
> f <- sd
> environment(f)
<environment: namespace:stats>
> find("f")
[1] ".GlobalEnv"

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

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

代码语言:javascript
复制
> 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调用了一个父环境。

代码语言:javascript
复制
> parent.env(e)
<environment: R_GlobalEnv>
> f(1:3)
[1] 1 

名称空间和包环境

这个原则也是软件包使用的“技巧”:

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

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

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

一个例子:

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

代码语言:javascript
复制
> 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首先在另一个函数的本地环境中查找,然后在调用函数的封闭环境中,然后一直到全局环境,即附加包的环境,直到它到达空环境。这就是“找不到的对象”bug睡觉的地方。

票数 21
EN

Stack Overflow用户

发布于 2017-06-08 16:44:12

environment(function)给出了函数的封闭环境(即闭包),该环境被指定为指向定义函数的环境的指针。这个约定称为词法作用域,它允许您使用模式(如工厂函数)。下面是一个简单的例子

代码语言:javascript
复制
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

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

代码语言:javascript
复制
assign("data",100,L$envir)
L$add()
#> 101

此外,当我们再次调用函数factory()时,将创建另一个新环境,并将其分配为函数调用中定义的函数的闭包,这使得我们必须将foo$add()函式作用域分离到它们自己的单独环境中:

代码语言:javascript
复制
M = factory()
M$add()
#> 1
#> 2
L$add()
#> 102

上面的工厂函数通过继续搜索变量(以及使用作用域赋值操作符)来说明函数与其包围环境之间的链接,而下面的函数通过承诺说明本地环境和调用框架之间的链接,这就是R在函数调用中传递变量的方式。

具体来说,当您调用一个函数时,R会为传递的变量和表达式的值创建承诺。当参数为force()'d或使用参数时,通过在调用环境中评估允诺来传递(复制)变量/表达式中的这些值--而不是更快!

例如,这个工厂函数接受一个作为承诺存储的参数,直到调用返回的函数为止:

代码语言:javascript
复制
factory2 <- function(x){
    out <-function(){
         return(x)
    }
    return(out)
}

现在,在某些情况下,factory2的行为是直观的:

代码语言:javascript
复制
y = 1
f = factory2(y)
f()
#> 1

但在其他情况下却并非如此:

代码语言:javascript
复制
y = 1
h = factory2(y)
y = 2
h()
#> 2

因为对表达式y的承诺在调用h()之前是不计算的,在第二个示例中,y的值是2!当然,既然通过允诺评估将值从调用环境复制到本地环境中,更改y的值不会影响h()返回的值。

代码语言:javascript
复制
y = 3
h()
#> 2
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44427752

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档