web-dev-qa-db-ja.com

関数内の関数内の変数にアクセスする

Rで関数を実行すると、その中で別の関数が実行されます。この行にコードがあります:

f_a <- function(b, c){
    return(b + c)
}

f_e <- function(){
    b = 2
    c = 2 
    d = f_a(b, c)
    print(d)
}

これは正常に動作します。私がしたいのは変数を渡さないb, cを関数f_aに渡すことです。私はこのようなことをしたいと思います(エラーをスローします)

f_a <- function(){
    return(b + c)
}

f_e <- function(){
    b = 2
    c = 2
    d = f_a()
    print(d)
}

環境や検索パスなどを使用してこれを行う方法はありますか?

10
Siddd

字句スコープ についてお読みになることをお勧めしますが、多くの変数を記述しないようにするための適切なアプローチは次のとおりです。

get_args_for <- function(fun, env = parent.frame(), inherits = FALSE, ..., dots) {
    potential <- names(formals(fun))

    if ("..." %in% potential) {
        if (missing(dots)) {
            # return everything from parent frame
            return(as.list(env))
        }
        else if (!is.list(dots)) {
            stop("If provided, 'dots' should be a list.")
        }

        potential <- setdiff(potential, "...")
    }

    # get all formal arguments that can be found in parent frame
    args <- mget(potential, env, ..., ifnotfound = list(NULL), inherits = inherits)
    # remove not found
    args <- args[sapply(args, Negate(is.null))]
    # return found args and dots
    c(args, dots)
}

f_a <- function(b, c = 0, ..., d = 1) {
    b <- b + 1
    c(b = b, c = c, d = d, ...)
}

f_e <- function() {
    b <- 2
    c <- 2
    arg_list <- get_args_for(f_a, dots = list(5))
    do.call(f_a, arg_list)
}

> f_e()
b c d   
3 2 1 5 

デフォルトでinherits = FALSEを設定すると、指定した環境からのみ変数を取得できます。また、dots = NULLを呼び出すときにget_args_forを設定して、すべての変数を渡すのではなく、省略記号を空のままにすることもできます。

それにもかかわらず、dotsが末尾に追加されるだけであり、一部の引数に名前が付けられていない場合、それらは位置によって一致してしまう可能性があるため、完全に堅牢ではありません。また、呼び出しで一部の値がNULLである必要がある場合、それを検出するのは簡単ではありません。


これらをRパッケージ内で使用しないことを強くお勧めします。見苦しいだけでなく、RのCMDチェックから未定義のグローバル変数に関する一連のメモが表示されます。

別のオプション。

f_a <- function() {
    return(b + c)
}

f_e <- function() {
    b <- 2
    c <- 2
    # replace f_a's enclosing environment with the current evaluation's environment
    environment(f_a) <- environment()
    d <- f_a()
    d
}

> f_e()
[1] 4

上記のようなものはおそらくRパッケージの内部では機能しないでしょう。なぜなら、パッケージの関数はそれを囲む環境がロックされていると思うからです。

または:

f_a <- function() {
    with(parent.frame(), {
        b + c
    })
}

f_e <- function() {
    b <- 2
    c <- 2
    f_a()
}

> f_e()
[1] 4

そうすれば、他の関数を囲む環境を永続的に変更する必要がなくなります。ただし、両方の関数が環境を共有するため、次のようなことが起こります。

f_a <- function() {
    with(parent.frame(), {
        b <- b + 1
        b + c
    })
}

f_e <- function() {
    b <- 2
    c <- 2
    d <- f_a()
    c(b,d)
}

> f_e()
[1] 3 5

内部関数を呼び出すと、外部環境の値が変更されます。

もう1つのオプションは、evalを使用して一時的に囲み環境を変更するだけなので、もう少し柔軟です。ただし、「daRk magic」を通じて現在の実行環境を検出する特定のR関数があり、evalに騙されることはできません。 この説明 を参照してください。

f_a <- function() {
    b <- b + 1
    b + c
}

f_e <- function() {
    b <- 2
    c <- 2
    # use current environment as enclosing environment for f_a's evaluation
    d <- eval(body(f_a), list(), enclos=environment())
    c(b=b, d=d)
}

> f_e()
b d 
2 5 
13
Alexis

1つのオプションは、呼び出し環境からaおよびbを明示的に取得することです。

f_a <- function(){
    get('b', envir = parent.frame()) + get('c', envir = parent.frame())
}

f_e <- function(){
    b = 2
    c = 2
    d = f_a()
    d
}

f_e()
#> [1] 4

または、quoteを使用して評価を遅らせ、次にevalを使用して呼び出し環境でコードを評価し、実質的に同じことを行うことができます。

f_a <- function(){
    eval(quote(b + c), parent.frame())
}

ただし、これはf_aを正常に呼び出すための可能な方法を制限するため、コードを記述するための堅牢な方法ではありません。変数を明示的に渡すコードを実行する方がはるかに簡単です。

4
alistaire

編集:

@alistaireがquoteを使用して式を作成するという提案は、次の代替案を提示しますが、見た目はそれほど醜くありません。

expr_env <- new.env()
   expr_env$f_a <- quote(b+c)
   expr_env$f_z <- quote(x+y)

f_e<-function(){
    b=2
    c=2
    d=eval( expr_env$f_a)
    print(d)
}

localを使用して関数を定義することは、受け入れ可能な代替手段ですか?

 f_e<-function(){
     b=2
     c=2
     d<-local({
          b+c
              })

     print(d)
 }
 f_e()
[1] 4

別の方法は、解析ツリーのみを返し、関数の「ローカル」環境での評価を終了することです。これは私にとって「醜い」ようです:

expr_list<-function(){  f_a <- quote(b+c)
                        f_z <- quote(x+y)
list(f_a=f_a,f_z=f_z) }

f_e<-function(){
    b=2
    c=2
    d=eval( (expr_list()$f_a))
    print(d)
}
2
42-

変数をグローバル環境に割り当てて、関数内で使用できます。

f_a <- function(){
    return(b + c)
}

f_e <- function(){
    assign("b", 2, envir = .GlobalEnv)
    assign("c", 2, envir = .GlobalEnv)
    d = f_a()
    print(d)
}

# > f_e()
# [1] 4
0