r - 为什么 R 不在提供的环境父树中查找指定的对象?

标签 r r-environment

配置:

OS : Windows 10 (64 bits)
R version: 3.6.3

我正在学习 R,目前我正在阅读 R 中的环境。我正在做一些练习,我想出了一个我自己创建的示例,但似乎我仍然无法解释和理解在 R 中正确查找对象的概念。一般来说,到目前为止我所理解的(如果我错了,请纠正我)是,如果 R 在当前环境中找不到对象,它会按顺序调用所有现有的父环境。只是为了看看它在实践中如何工作,我创建了以下程序:

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 中定义的,并且 e4e10 的祖先,我预计为了找到 testvar 的值,R 在父树中从 e10 上升到 e4。但程序因以下错误而停止:

Error in e10$testfun() (from #3) : object 'testvar' not found

你能告诉我我误解了什么吗?我使用 with(data = e10, ...) 的事实不应该意味着用于函数调用的环境是 e10?

最佳答案

所以,这是一个异常微妙的问题。您需要在这里考虑两种相关类型的环境,绑定(bind)环境,或者与您的函数绑定(bind)的环境,以及封闭环境,或者创建函数的环境。在本例中,绑定(bind)环境是 e10,但封闭环境是全局环境。来自 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

我希望这能澄清这个问题。

更新:确定封闭和绑定(bind)环境

那么我们如何确定诸如 testfun() 之类的函数的绑定(bind)和封闭环境?

G. Grothendieck's answer showsenvironment() 函数为您提供函数的封闭环境:

environment(e10$testfun)
# <environment: R_GlobalEnv>

据我所知,基础 R 中没有一个简单的函数可以为您提供函数的绑定(bind)环境。如果您要查找的函数位于父环境中,则可以使用 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>

关于r - 为什么 R 不在提供的环境父树中查找指定的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61568068/

相关文章:

r - Matlab 和 R 中的相同随机种子

r - 将置信区间添加到分位数回归的样条曲线

python - 如何在 plotly 中获取所有色标的名称?

r - 在受限环境中使用 `rmarkdown::render`

从 R 函数返回列表与环境

r - 如何检查环境是否是包命名空间

r - 使用 dplyr 包时保留逗号类

r - 如何使用 RSelenium 在网页中同时按下两个键(即 control-s)?

renv:restore() 在 Windows 中总是失败