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

时间:2017-06-08 06:01:37

标签: r function environment

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

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

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

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

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

根据Advanced R by Hadley Wickham:“封闭的环境属于该功能,永远不会改变......”

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

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

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

总结我的问题:

  1. 我们真的可以改变函数的封闭环境吗?
  2. stats包的两种不同环境是什么?
  3. 功能的环境是什么?
  4. 它类似于上一篇文章in here

2 个答案:

答案 0 :(得分:15)

TLDR:

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

    在谈论某项功能时,您必须区分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"
    

    这里发生了什么?封闭的环境仍然是命名空间&#39;&#39; stats&#39;&#39;。那就是创建函数的地方。但是,绑定环境现在是全球环境。那个名字&#34; f&#34;与对象绑定。

    我们可以将封闭环境更改为新环境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 
    

    命名空间和程序包环境

    这个原则也是&#34;技巧&#34;包使用:

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

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

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

    插图:

    enter image description here

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

    > 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首先在该其他函数的本地环境中查找,然后在调用函数的封闭环境中,以及一直到全局环境,附加包的环境直到它到达空的环境。没有找到&#34;对象的地方&#34;虫子睡觉。

答案 1 :(得分:3)

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

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

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

此外,当我们再次调用函数factory()时,会创建另一个新环境 并被指定为在其中定义的函数的闭包 函数调用,这是我们必须分离foo $ add()函数的原因 它们适用于各自独立的环境:

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

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

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

例如,此工厂函数接受一个参数,该参数存储为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之前不会评估表达式h()的承诺,而在第二个示例中,y的值为2!当然,现在已经通过Promise评估将值从调用环境复制到本地环境中,更改y的值不会影响h()返回的值:

y = 3
h()
#> 2