为什么 R 不在提供的环境父树中查找指定的对象?
Why doesn't R look up for the specified object in the provided envrionment parents tree?
配置:
OS : Windows 10 (64 bits)
R version: 3.6.3
我正在学习 R,目前正在阅读 R 中的环境。我正在做一些练习,想出了一个自己创建的示例,但似乎我仍然无法解释并正确理解在 R 中查找对象的概念。一般来说,到目前为止我所理解的(如果我错了请纠正我)是,如果 R 在当前环境中找不到对象,它会调用 in order all现有的父环境。只是为了看看它在实践中是如何工作的,我创建了以下程序:
library(rlang)
library(envnames)
library(lobstr)
e1 <- env()
e2 <- new_environment(parent = e1)
e3 <- new_environment(parent = e2)
e4 <- new_environment(parent = e3)
e5 <- new_environment(parent = e4)
e6 <- new_environment(parent = e5)
e7 <- new_environment(parent = e6)
e8 <- new_environment(parent = e7)
e9<- new_environment(parent = e8)
e10 <- new_environment(parent = e9)
e4$testvar <- 1200
e10$testfun <- function(x) {
print(envnames::environment_name(caller_env()))
return (testvar)
}
下面是我如何 运行 通过选择 e10 作为调用者环境
上面的程序
with(data = e10, expr = e10$testfun())
假设 testvar 在环境 e4 中定义并且 e4 是e10,我希望 R 在父树中从 e10 上升到 e4,以便找到 testvar 的值。但是程序因以下错误而停止:
Error in e10$testfun() (from #3) : object 'testvar' not found
你能告诉我我误解了什么吗?我使用 with(data = e10, ...)
的事实并不意味着用于函数调用的环境是 e10?
所以,这是一个异常微妙的问题。这里有两种相关的环境类型需要考虑,binding 环境,或者绑定到你的函数的环境,以及 enclosing 环境,或者创建函数的环境。在这种情况下,绑定环境是 e10
,但 enclosing 环境是全局环境。来自 Hadley Wickham's Advanced R:
The enclosing environment belongs to the function, and never changes, even if the function is moved to a different environment. The enclosing environment determines how the function finds values; the binding environments determine how we find the function.
考虑以下(在执行您提供的代码后执行的)演示这一点:
eval(expression(testfun()), envir = e10)
# [1] "e10"
# Error in testfun() : object 'testvar' not found
testvar <- 600
eval(expression(testfun()), envir = e10)
# [1] "e10"
# [1] 600
此外,现在考虑:
eval(envir = e10, expr = expression(
testfun2 <- function(x) {
print(envnames::environment_name(caller_env()))
return (testvar)
}
))
eval(expression(testfun2()), envir = e10)
# [1] "e10"
# [1] 1200
我希望这能澄清问题。
更新:确定封闭和绑定环境
那么我们如何确定testfun()
等函数的绑定和封闭环境?
与一样,environment()
函数为您提供函数的封闭环境:
environment(e10$testfun)
# <environment: R_GlobalEnv>
据我所知,基础 R 中没有简单的函数可以为您提供函数的绑定环境。如果您要查找的函数在父环境中,则可以使用 pryr::where()
:
pryr::where("mean")
# <environment: base>
(有一个base
函数来查看一个函数是否在一个环境中,exists()
,pryr::where()
使用它。但是,它不会通过父环境递归喜欢 where()
.)
但是,如果您必须搜索子环境,据我所知没有相应的功能。但是,模拟一个似乎很简单:
get_binding_environments <- function(fname) {
## First we need to find all the child environments to search through.
## We don't want to start from the execution environment;
## we probably want to start from the calling environment,
## but you may want to change this to the global environment.
e <- parent.frame()
## We want to get all of the environments we've created
objects <- mget(ls(envir = e), envir = e)
environments <- objects[sapply(objects, is.environment)]
## Then we use exists() to see if the function has a binding in any of them
contains_f <- sapply(environments, function(E) exists(fname, where = E))
return(unique(environments[contains_f]))
}
get_binding_environments("testfun")
# [[1]]
# <environment: 0x55f865406518>
e10
# <environment: 0x55f865406518>
问题中的代码在全局环境中定义了一个函数。我们可以这样查询它的环境:
environment(e10$testfun)
## <environment: R_GlobalEnv>
当函数查找自由变量(被引用但未在函数中定义的变量)时,例如 testvar
,它会使用函数的环境(和祖先)来查找变量。 testvar
在 e4
中,但 e4
不是全局环境的祖先,因此在问题中找不到 testvar
。
其他环境无关紧要。如果调用该函数,则调用者的环境(也称为父框架)在变量查找中根本没有任何作用。同样,如果该函数后来被放置在其他地方(在本例中为 e10
),则该环境也不会参与变量查找。此外,意识到当问题中的函数被放入 e10
时,它已经在全局环境中定义,因此全局环境已经被设置为它的环境。函数由参数、主体和环境(以及 class 等属性)组成,将函数移动到其他地方不会改变任何这些成分。
修复
如果我们想让 testfun
的环境成为它的环境并因此将 e4
作为祖先:
environment(e10$testfun) <- e10
或者,我们无法首先在全局环境中定义 testfun
,而是从一开始就在 e10
环境中定义 testfun
:
with(e10, {
testfun <- function() testvar
})
e10$testfun()
## [1] 1200
函数名称
另一个混淆点可能是误以为下面的语句定义了e10
中名为testfun
的函数。
e10$testfun <- function() testvar
这个想法的问题是函数没有名字。函数的三个组成部分是参数、主体和环境(以及 class 和可能的 srcref 和 scrfile 等属性)。该名称不是函数的组成部分。可以将一个函数放在一个变量中并引用该变量,就好像它是函数的名称一样,但实际上它只是一个保存函数的变量,名称不是函数本身的一部分。因此,在上面的代码行中,我们没有定义名为 testfun 的函数;相反,我们正在定义一个匿名函数(在全局环境中),然后将其移动到变量 testfun
.
R 本身的一个例子
虽然用户创建的函数通常保留在定义它们的环境中,例如
f <- function() "hello"
# the environment of f
environment(f)
## <environment: R_GlobalEnv>
# the environment where f is located (same)
as.environment(find("f"))
## <environment: R_GlobalEnv>
搜索路径上的 R 包中的函数
# show search path
search()
通常不位于他们的环境中。对于搜索路径上包中的任何函数,该函数将以包的命名空间作为其环境,但是当您访问该函数时,它将被发现,而不是在命名空间中,而是在不同的环境中。
# the environment of function mean
e1 <- environment(mean); e1
## <environment: namespace:base>
# where mean is located
e2 <- as.environment(find("mean")); e2
## <environment: base>
# these are NOT the same
identical(e1, e2)
## [1] FALSE
此博客中的图表很好地说明了这一点 post:http://blog.obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/
原型
有一个包可以按照问题预期的方式工作。 proto 对象就像环境,但如果您为它们分配一个函数,那么该函数的环境将更改为它们分配给的 proto object/environment。 (还有其他差异,但我们重点关注这一点。)
首先我们定义一个原型对象p
,其父对象是e9,然后将感兴趣的函数赋值给p
。最后我们运行那个函数。 (第一个参数隐式是 proto 对象,所以我们忽略了它。)我们看到函数确实重置了它的环境,并且 e4 现在是它的环境的祖先,没有明确设置它。
library(proto)
p <- proto(e9) # define proto object whose parent is e9
p$testfun <- function(self) testvar
identical(p, with(p, environment(testfun))) # testfun's environment is now p
## [1] TRUE
p$testfun()
## [1] 1200
配置:
OS : Windows 10 (64 bits)
R version: 3.6.3
我正在学习 R,目前正在阅读 R 中的环境。我正在做一些练习,想出了一个自己创建的示例,但似乎我仍然无法解释并正确理解在 R 中查找对象的概念。一般来说,到目前为止我所理解的(如果我错了请纠正我)是,如果 R 在当前环境中找不到对象,它会调用 in order all现有的父环境。只是为了看看它在实践中是如何工作的,我创建了以下程序:
library(rlang)
library(envnames)
library(lobstr)
e1 <- env()
e2 <- new_environment(parent = e1)
e3 <- new_environment(parent = e2)
e4 <- new_environment(parent = e3)
e5 <- new_environment(parent = e4)
e6 <- new_environment(parent = e5)
e7 <- new_environment(parent = e6)
e8 <- new_environment(parent = e7)
e9<- new_environment(parent = e8)
e10 <- new_environment(parent = e9)
e4$testvar <- 1200
e10$testfun <- function(x) {
print(envnames::environment_name(caller_env()))
return (testvar)
}
下面是我如何 运行 通过选择 e10 作为调用者环境
上面的程序with(data = e10, expr = e10$testfun())
假设 testvar 在环境 e4 中定义并且 e4 是e10,我希望 R 在父树中从 e10 上升到 e4,以便找到 testvar 的值。但是程序因以下错误而停止:
Error in e10$testfun() (from #3) : object 'testvar' not found
你能告诉我我误解了什么吗?我使用 with(data = e10, ...)
的事实并不意味着用于函数调用的环境是 e10?
所以,这是一个异常微妙的问题。这里有两种相关的环境类型需要考虑,binding 环境,或者绑定到你的函数的环境,以及 enclosing 环境,或者创建函数的环境。在这种情况下,绑定环境是 e10
,但 enclosing 环境是全局环境。来自 Hadley Wickham's Advanced R:
The enclosing environment belongs to the function, and never changes, even if the function is moved to a different environment. The enclosing environment determines how the function finds values; the binding environments determine how we find the function.
考虑以下(在执行您提供的代码后执行的)演示这一点:
eval(expression(testfun()), envir = e10)
# [1] "e10"
# Error in testfun() : object 'testvar' not found
testvar <- 600
eval(expression(testfun()), envir = e10)
# [1] "e10"
# [1] 600
此外,现在考虑:
eval(envir = e10, expr = expression(
testfun2 <- function(x) {
print(envnames::environment_name(caller_env()))
return (testvar)
}
))
eval(expression(testfun2()), envir = e10)
# [1] "e10"
# [1] 1200
我希望这能澄清问题。
更新:确定封闭和绑定环境
那么我们如何确定testfun()
等函数的绑定和封闭环境?
与environment()
函数为您提供函数的封闭环境:
environment(e10$testfun)
# <environment: R_GlobalEnv>
据我所知,基础 R 中没有简单的函数可以为您提供函数的绑定环境。如果您要查找的函数在父环境中,则可以使用 pryr::where()
:
pryr::where("mean")
# <environment: base>
(有一个base
函数来查看一个函数是否在一个环境中,exists()
,pryr::where()
使用它。但是,它不会通过父环境递归喜欢 where()
.)
但是,如果您必须搜索子环境,据我所知没有相应的功能。但是,模拟一个似乎很简单:
get_binding_environments <- function(fname) {
## First we need to find all the child environments to search through.
## We don't want to start from the execution environment;
## we probably want to start from the calling environment,
## but you may want to change this to the global environment.
e <- parent.frame()
## We want to get all of the environments we've created
objects <- mget(ls(envir = e), envir = e)
environments <- objects[sapply(objects, is.environment)]
## Then we use exists() to see if the function has a binding in any of them
contains_f <- sapply(environments, function(E) exists(fname, where = E))
return(unique(environments[contains_f]))
}
get_binding_environments("testfun")
# [[1]]
# <environment: 0x55f865406518>
e10
# <environment: 0x55f865406518>
问题中的代码在全局环境中定义了一个函数。我们可以这样查询它的环境:
environment(e10$testfun)
## <environment: R_GlobalEnv>
当函数查找自由变量(被引用但未在函数中定义的变量)时,例如 testvar
,它会使用函数的环境(和祖先)来查找变量。 testvar
在 e4
中,但 e4
不是全局环境的祖先,因此在问题中找不到 testvar
。
其他环境无关紧要。如果调用该函数,则调用者的环境(也称为父框架)在变量查找中根本没有任何作用。同样,如果该函数后来被放置在其他地方(在本例中为 e10
),则该环境也不会参与变量查找。此外,意识到当问题中的函数被放入 e10
时,它已经在全局环境中定义,因此全局环境已经被设置为它的环境。函数由参数、主体和环境(以及 class 等属性)组成,将函数移动到其他地方不会改变任何这些成分。
修复
如果我们想让 testfun
的环境成为它的环境并因此将 e4
作为祖先:
environment(e10$testfun) <- e10
或者,我们无法首先在全局环境中定义 testfun
,而是从一开始就在 e10
环境中定义 testfun
:
with(e10, {
testfun <- function() testvar
})
e10$testfun()
## [1] 1200
函数名称
另一个混淆点可能是误以为下面的语句定义了e10
中名为testfun
的函数。
e10$testfun <- function() testvar
这个想法的问题是函数没有名字。函数的三个组成部分是参数、主体和环境(以及 class 和可能的 srcref 和 scrfile 等属性)。该名称不是函数的组成部分。可以将一个函数放在一个变量中并引用该变量,就好像它是函数的名称一样,但实际上它只是一个保存函数的变量,名称不是函数本身的一部分。因此,在上面的代码行中,我们没有定义名为 testfun 的函数;相反,我们正在定义一个匿名函数(在全局环境中),然后将其移动到变量 testfun
.
R 本身的一个例子
虽然用户创建的函数通常保留在定义它们的环境中,例如
f <- function() "hello"
# the environment of f
environment(f)
## <environment: R_GlobalEnv>
# the environment where f is located (same)
as.environment(find("f"))
## <environment: R_GlobalEnv>
搜索路径上的 R 包中的函数
# show search path
search()
通常不位于他们的环境中。对于搜索路径上包中的任何函数,该函数将以包的命名空间作为其环境,但是当您访问该函数时,它将被发现,而不是在命名空间中,而是在不同的环境中。
# the environment of function mean
e1 <- environment(mean); e1
## <environment: namespace:base>
# where mean is located
e2 <- as.environment(find("mean")); e2
## <environment: base>
# these are NOT the same
identical(e1, e2)
## [1] FALSE
此博客中的图表很好地说明了这一点 post:http://blog.obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/
原型
有一个包可以按照问题预期的方式工作。 proto 对象就像环境,但如果您为它们分配一个函数,那么该函数的环境将更改为它们分配给的 proto object/environment。 (还有其他差异,但我们重点关注这一点。)
首先我们定义一个原型对象p
,其父对象是e9,然后将感兴趣的函数赋值给p
。最后我们运行那个函数。 (第一个参数隐式是 proto 对象,所以我们忽略了它。)我们看到函数确实重置了它的环境,并且 e4 现在是它的环境的祖先,没有明确设置它。
library(proto)
p <- proto(e9) # define proto object whose parent is e9
p$testfun <- function(self) testvar
identical(p, with(p, environment(testfun))) # testfun's environment is now p
## [1] TRUE
p$testfun()
## [1] 1200