R中环境的图片表示? [关闭]

问题描述 投票:1回答:2

请你帮我理解书面形象(环境如何运作)?

 enclosing <- function() {
 z <- 2
 function(x, y = x) {
x + y + z
  }
 }

 f <- enclosing()
 calling <- function() {
 w <- 5
 f(x = 2 * w)
}
calling()
## [1] 22

本书中的解释如下:您可以在全局环境中开始定义封闭作为函数。当您调用封闭时,您将创建一个评估环境,在该环境中存储变量z,然后将存储在全局环境中的函数返回为f。由于此函数是在封闭的评估环境中定义的,因此该环境是f的环境。然后创建调用,将其存储在全局环境中并调用它。这再次创造了一个评估环境。在此,您存储变量w,然后调用f。您在评估环境中没有f,但由于评估环境的父级是全局环境,您可以找到它。当你调用f时,你给它表达式2 * w作为参数x。在f的调用中,您有另一个评估环境。它的父母是封闭你的封闭。在这里你需要评估f的身体:x + y + z。但是,在此之前,需要建立评估环境。由于x和y是形式参数,因此它们将作为promises存储在评估环境中。在调用f时,您提供了x作为参数,因此必须在调用环境(调用内部环境)中评估此promise,而y具有默认值,因此必须在评估环境中对其进行评估。在这种环境中,它可以看到x和y以及通过父环境z。您评估x,它是调用环境中的表达式2 * w,其中w是已知的,并且您在本地环境中评估y,其中x是已知的。因此,您可以获取这两个变量的值,然后从封闭环境中获取z。

我的尝试是这样的:

Environment representation

r environment
2个回答
1
投票

environment tree

环境层次结构显示在下面的树中。也就是说,封闭和召唤是全球环境的子女,匿名(等于f)是封闭的孩子。

Environment hierarchy

.GlobalEnv
    enclosing
        anonymous == f
    calling

我们可以通过检测这样的函数来验证这些关系:

library(pryr)

enclosing <- function() {
  e <- environment(); attr(e, "name") <- "enclosing"
  cat("envir:", environmentName(e), 
      "address:", address(e),
       "parent:", environmentName(parent.env(e)), 
       "parent frame:", environmentName(parent.frame())), "\n")
  z <- 2
  function(x, y = x) {
    e <- environment(); attr(e, "name") <- "anon"
    cat("envir:", environmentName(e), 
        "address:", address(e),
       "parent:", environmentName(parent.env(e)), 
       "parent frame:", environmentName(parent.frame()), "\n")
    x + y + z
  }
 }

 f <- enclosing()
 calling <- function() {
   e <- environment(); attr(e, "name") <- "calling"
   cat("envir:", environmentName(e), 
       "address:", address(e),
       "parent:", environmentName(parent.env(e)), 
       "parent frame:", environmentName(parent.frame()), "\n")

   w <- 5
   f(x = 2 * w)
}
calling()

运行上述的输出是:

envir: enclosing address: 0x11ee1fd8 parent: R_GlobalEnv parent frame: R_GlobalEnv 
envir: calling address: 0x8da2ee0 parent: R_GlobalEnv parent frame: R_GlobalEnv 
envir: anon address: 0x8da0ae8 parent: enclosing parent frame: calling 
[1] 22

以上输出显示

  • 在调用函数时创建的运行时环境(或评估环境)
  • 它在内存中的地址 - 如果你运行它,你的地址会有所不同
  • 运行时环境的父(或封闭环境)
  • 调用者的环境或父框架

每个环境都有一个父项,输出显示封闭和调用的父项是全局环境,而anon的父项是封闭的。

在上面,每个函数被调用一次;但是,如果一个函数被调用两次,那么运行时环境每次都会不同。我们可以告诉它,因为即使我们已经使用相同的名称命名了两个实例,来自检测输出的地址也会有所不同。

例如,每次调用calling时,都会为该执行实例创建一个新的运行时环境,上面的代码将命名该运行时环境calling,但我们可以通过它们的不同地址来区分这两个实例。

另一方面,运行时环境的父级是定义函数的环境,因此它不会从一次调用更改为下一次调用。因此,如果我们两次调用calling,则会创建两个不同的运行时环境 - 来自检测代码的输出会将它们命名为calling,但我们可以区分它们,因为它们在输出中将具有不同的地址。自从calling在全球环境中定义以来,这两个调用都将全局环境作为其父级。

请注意,f被赋予了在enclosing中定义的匿名函数,因此即使名称f的赋值发生在全局环境中,其定义(参数,正文等)也发生在enclosing中。

请注意,函数的运行时环境有时称为evaluation environment,父环境有时称为enclosing environment

您可能想要查看的有用博客文章是http://blog.obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/

function calls

父帧是调用者的环境,即调用当前函数的函数的执行实例中的运行时环境。父框架不一定与父框架相同。当R搜索对象时,它在当前环境中搜索,如果没有找到,则它在父环境中查找,如果仍然没有在祖先树中找到。 R不在父框架中搜索,因此父框架及其调用树与环境的层次结构无关。

Call tree

  .GlobalEnv
    enclosing
    calling
      anonymous == f

0
投票

我认为我们可以通过一个例子更好地理解全球,封闭和评估环境之间的关系。我稍微修改了OP的例子。

z的值不存储在全局环境中,它仍然在调用之间持续存在。 z的值存储在封闭环境中。

enclosing <- function(a) {
  z <- a
  function(x, y = x) {
    z <- x + y + z  #This value z will not persist between calls of f.
  }
}

f <- enclosing(2)
calling <- function() {
  w <- 5
  f(x = 2 * w)
}
n <- calling()  #no change in value of z
## [1] 22
n <- calling()
## [1] 22        value of z persisted in enclosing environment between call.
f <- enclosing(4) #value of z is changed to 4 now
n <- calling()
## [1] 24
n <- calling()
## [1] 24       value of z persisted in enclosing environment between call.
© www.soinside.com 2019 - 2024. All rights reserved.