【问题标题】:Environments in R, mapply and getR中的环境,mapply和get
【发布时间】:2014-04-15 14:17:41
【问题描述】:

x<-2在全局环境中:

x <-2 
x
[1] 2

a成为一个函数,在本地定义另一个x并使用get

a<-function(){
  x<-1
  get("x")
}

此函数正确地从本地环境中获取x

a()
[1] 1

现在让我们定义一个函数b 如下,它使用mapplyget

b<-function(){
  x<-1
  mapply(get,"x")
}

如果我调用b,似乎mapply 使get 没有先搜索函数环境。相反,它会尝试直接从全局环境中获取x,如果x 未在全局环境中定义,则会给出错误消息:

b()
x 
2 
rm(x)
b()
Error in (function (x, pos = -1L, envir = as.environment(pos), mode = "any",  : 
  object 'x' not found 

解决方案是显式定义envir=environment()

c<-function(){
  x<-1
  mapply(get,"x", MoreArgs = list(envir=environment()))
}

c()
x 
1 

但我想知道这里到底发生了什么。 mapply在做什么? (为什么?这是预期的行为吗?)这种“陷阱”在其他 R 函数中是否常见?

【问题讨论】:

    标签: r get environment mapply


    【解决方案1】:

    问题在于get 会查看其调用的环境,但在这里我们将get 传递给mapply,然后从mapply 中的本地环境调用get。如果在mapply 本地环境中找不到x,那么它会查看该环境的父环境,即查看environment(mapply)(这是定义mapply 的词法环境,其中是基本命名空间环境);如果它也不存在,它会查看它的父级,即全局环境,即您的 R 工作区。

    这是因为 R 使用 词法 作用域,as opposed to dynamic scoping

    我们可以通过获取一个存在于mapply 中的变量来证明这一点。

     x <- 2
     b2<-function(){
       x<-1
       mapply(get, "USE.NAMES")
     }
     b2() # it finds USE.NAMES in mapply
     ## USE.NAMES 
     ##     TRUE 
    

    除了问题中显示的涉及MoreArgs 的解决方法之外,这也有效,因为它会导致搜索在未能找到mapply 后查看b 内的本地环境。 (这只是为了说明正在发生的事情,在实际实践中,我们更喜欢问题中显示的解决方法。)

    x <- 2
    b3 <-function(){
       x<-1
       environment(mapply) <- environment()
       mapply(get, "x")
    }
    b3()
    ## 1
    

    添加 扩展说明。另请注意,我们可以像这样查看环境链:

    > debug(get)
    > b()
    debugging in: (function (x, pos = -1L, envir = as.environment(pos), mode = "any", 
        inherits = TRUE) 
    .Internal(get(x, envir, mode, inherits)))(dots[[1L]][[1L]])
    debug: .Internal(get(x, envir, mode, inherits))
    Browse[2]> envir
    <environment: 0x0000000021ada818>
    Browse[2]> ls(envir) ### this shows that envir is the local env in mapply
    [1] "dots"      "FUN"       "MoreArgs"  "SIMPLIFY"  "USE.NAMES"
    Browse[2]> parent.env(envir) ### the parent of envir is the base namespace env
    <environment: namespace:base>
    Browse[2]> parent.env(parent.env(envir)) ### and grandparent of envir is the global env
    <environment: R_GlobalEnv>
    

    因此,可能遵循的环境的祖先是这样的(箭头指向父级的地方):

    local environment within mapply --> environment(mapply) --> .GlobalEnv
    

    其中environment(mapply) 等于asNamespace("base"),基本命名空间环境。

    【讨论】:

    • 换句话说,这是动态和词法作用域之间的冲突。
    • 其实不太对。 identical((function() { mapply(function(x) environment(), 1) })()[[1]], baseenv())FALSE。在environment(mapply) 中看起来并非如此。从the source code 可以看出,问题在于对内置 C 函数 eval 的纯粹调用,在这种情况下,它直接从 baseenv() 创建自己的临时环境分支
    • @Robert,相关计算实际上是 trace(get, quote({print(identical(parent.env(envir), environment(mapply))); print(identical(environment(mapply), asNamespace("base")))})) ; b(),它产生两个 TRUE 值 - 即 getenvir arg 的父级确实是 environment(mapply) 或等效的 asNamespace("base")。确实,对基本环境的引用更准确地称为基本命名空间环境(而不仅仅是基本环境),尽管它们经常相互标识。为了准确起见,我添加了命名空间这个词。 `
    【解决方案2】:

    R 是词法范围的,而不是动态范围的,这意味着当您搜索父环境以查找值时,您正在搜索词法父环境(如源代码中所写),而不是动态父环境(如调用) .考虑这个例子:

    x <- "Global!"
    fun1 <- function() print(x)
    fun2 <- function() {
      x <- "Local!"
      fun1a <- function() print(x)
      fun1()           # fun2() is dynamic but not lexical parent of fun1()
      fun1a()          # fun2() is both dynamic and lexical parent of fun1a() 
    }
    fun2()
    

    输出:

    [1] "Global!"
    [1] "Local!"
    

    在这种情况下,fun2fun1a 的词法父级,但不是 fun1 的词法父级。由于mapply 没有在你的函数内部定义,你的函数不是mapply 的词法父级,mapply 不能直接访问其中定义的xs。

    【讨论】:

      【解决方案3】:

      问题在于与内置 C 代码的相互作用。即,考虑以下几点:

      fx <- function(x) environment()
      env <- NULL; fn <- function() { env <<- environment(); mapply(fx, 1)[[1]] }
      

      然后

      env2 <- fn()
      identical(env2, env)
      # [1] FALSE
      identical(parent.env(env2), env)
      # [1] FALSE
      identical(parent.env(env2), globalenv())
      # [1] TRUE
      

      更具体地说,问题出在underlying C code 上,它没有考虑执行环境,并将其交给原样的underlying C eval 调用,该调用创建了一个直接从R_GlobalEnv 分支的临时环境。

      请注意,这确实是正在发生的事情,因为没有任何级别的堆栈嵌套可以解决问题:

      env <- NULL; fn2 <- function() { env <<- environment(); (function() { mapply(fx, 1)[[1]] })() }
      identical(parent.env(fn2()), globalenv())
      # [1] TRUE
      

      【讨论】:

      • Robert,我不熟悉底层 C 代码,但词法参数没有解释您在此处看到的行为吗?由于fx是在全局环境中定义的,所以它的父环境总是全局环境,无论你在哪里调用它。
      • 我明白了!我相信你是对的。我正在用手机写信,但我们可以通过嵌套fx 而不是fn2 的堆栈来确认这个假设。你能试试吗?现在应该有一个由两个环境组成的链,直到 globalenv。
      • fx &lt;- function(x) (function() environment())(); parent.env(parent.env(fn())) 返回&lt;environment: R_GlobalEnv&gt;
      猜你喜欢
      • 2020-12-10
      • 1970-01-01
      • 1970-01-01
      • 2021-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多