2

x<-2在全球环境中:

x <-2 
x
[1] 2

让是一个在本地a定义另一个并使用的函数:xget

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

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

a()
[1] 1

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

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 函数中是否常见?

4

3 回答 3

3

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!"

在这种情况下fun2,是 的词汇父级fun1a,但不是 的fun1。由于mapply未在您的函数内部定义,因此您的函数不是 s 的词法父级,mapply并且x其中定义的 s 不能直接访问mapply.

于 2014-04-15T14:56:52.887 回答
3

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

这是因为 R 使用词法作用域,而不是动态作用域

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

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

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

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)equalsasNamespace("base")是基本命名空间环境。

于 2014-04-15T14:48:03.807 回答
0

问题是与内置 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

更具体地说,问题在于底层 C 代码,它没有考虑执行环境,并将其交给一个原样的底层 Ceval调用,该调用创建一个直接从R_GlobalEnv.

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

env <- NULL; fn2 <- function() { env <<- environment(); (function() { mapply(fx, 1)[[1]] })() }
identical(parent.env(fn2()), globalenv())
# [1] TRUE
于 2014-04-15T15:00:01.920 回答