1

構成:

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で定義されており、e4がe10の祖先であることを考えると、 testvarの値を見つけるために、R が親ツリーで e10 から e4 まで上がると予想しました。しかし、プログラムは次のエラーで停止します。

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

私が誤解していたことを教えていただけますか?私が使用するという事実with(data = e10, ...)は、関数呼び出しに使用される環境が e10 であることを意味するべきではありませんか?

4

2 に答える 2

2

したがって、これは非常に微妙な問題です。ここで考慮する必要がある関連する 2 つのタイプの環境があります。バインド環境 (関数にバインドされている環境) と、エンクロージング環境 (関数が作成された環境) です。この場合、バインディング環境は ですe10が、エンクロージング環境はグローバル環境です。Hadley Wickham の Advanced Rから:

囲んでいる環境は関数に属し、関数が別の環境に移動されても変更されません。囲んでいる環境によって、関数が値を見つける方法が決まります。バインディング環境によって、関数を見つける方法が決まります。

これを示す次の (提供されたコードを実行した後に実行される) ことを検討してください。

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()でしょうか?

G. Grothendieck の回答が示すように、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>
于 2020-05-03T00:37:44.937 に答える