2013-01-18 11 views
23

更新2 @G。 Grothendieckは2つのアプローチを掲示しました。 2つ目は、関数内の関数環境を変更することです。これは、あまりにも多くのコーディング複製の問題を解決します。スクリプトをパッケージ化するときにCRANチェックをパスするのがよいかどうかはわかりません。私はいくつかの結論があるときに私は再び更新されます。Rでは、関数内の変数を関数内の下位レベルの関数で使用する方法を教えてください。(with、attach、environment)

更新

私はf5withf6を使用しようとした理由があり、env$c, env$d, env$callsとして関数内のすべての変数をf2への入力引数の変数の多くを渡すために、インデックスにはしたくないしようとしています(変更されたf2)。しかし、assignwithassignを移動、{}内部withでは動作しない仕事をするが、私の実際のケースでは、私は簡単にwith機能のうち、それらを移動する方法がわからないwith式の内部数assign秒を持っています。ここで

は一例です:

## In the <environment: R_GlobalEnv> 
a <- 1 
b <- 2 
f1 <- function(){ 
    c <- 3 
d <- 4 
f2 <- function(P){ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
} 
calls <- 0 
v <- vector() 
for(i in 1:10){ 
    v[i] <- f2(P=0) 
    c <- c+1 
    d <- d+1 
    } 
return(v) 
} 
f1() 

機能f2f2が呼び出されたときに、f1内にある、それは環境environment(f1)の変数calls,c,dを探します。これは私が欲しかったものです。 を他の関数でも使用したい場合は、代わりにグローバル環境でこの関数を定義します。f4とします。

f4 <- function(P){ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
} 

それはグローバル環境ではなく、関数が呼び出される関数の内部でcalls,c,dを探しますので、これは、動作しません。例えば:

f3 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    v[i] <- f4(P=0) ## or replace here with f5(P=0) 
    c <- c+1 
    d <- d+1 
    } 
    return(v) 
} 
f3() 

安全な方法は、f4の入力引数でcalls,c,dを定義する必要があり、その後、f4にこれらのパラメータを渡します。しかし、私の場合、この関数に渡す変数が多すぎるf4それは環境として渡すことができ、f4は地球環境(environment(f4))で見ていないと言うと、environmentf3が呼び出されたとき。

私が今解決する方法は、環境をリストとして使用し、with機能を使用することです。 assignは、元のオブジェクトを変更しないので、それがあるべきよう

f5 <- function(P,liste){ 
    with(liste,{ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
    } 
) 
} 
f3 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    v[i] <- f5(P=0,as.list(environment())) ## or replace here with f5(P=0) 
    c <- c+1 
    d <- d+1 
    } 
    return(v) 
} 
f3() 

しかし、今assign("calls", calls+1, inherits=TRUE)は動作しません。変数callsは、目的関数がf5である最適化関数に接続されています。これが入力引数としてcallsを渡す代わりにassignを使用する理由です。 attachを使っても私には分かりません。ここでassign問題を修正するための私の方法は次のとおりです。

f7 <- function(P,calls,liste){ 
    ##calls <<- calls+1 
    ##browser() 
    assign("calls", calls+1, inherits=TRUE,envir = sys.frame(-1)) 
    print(calls) 
    with(liste,{ 
    print(paste('with the listed envrionment, calls=',calls)) 
    return(P+c+d) 
    } 
) 
} 
######## 
################## 
f8 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    ##browser() 
    ##v[i] <- f4(P=0) ## or replace here with f5(P=0) 
    v[i] <- f7(P=0,calls,liste=as.list(environment())) 
    c <- c+1 
    d <- d+1 
    } 
    f7(P=0,calls,liste=as.list(environment())) 
    print(paste('final call number',calls)) 
    return(v) 
} 
f8() 

私がチェックCRANを通過する場合は特に、これは正しい方向にR.アムIで行われるべきかどうかはわかりませんよ?誰もがこれについていくつかのヒントを持っていますか?

答えて

22

(1)発信者の環境を渡します。親環境とインデックスを明示的に渡すことができます。

f2a <- function(P, env = parent.frame()) { 
    env$calls <- env$calls + 1 
    print(env$calls) 
    return(P + env$c + env$d) 
} 

a <- 1 
b <- 2 
# same as f1 except f2 removed and call to f2 replaced with call to f2a 
f1a <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2a(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1a() 

(2)関数の環境呼ばリセットがここに示されているようf1bf2bの環境をリセットすることです:

f2b <- function(P) { 
    calls <<- calls + 1 
    print(calls) 
    return(P + c + d) 
} 

a <- 1 
b <- 2 
# same as f1 except f2 removed, call to f2 replaced with call to f2b 
# and line marked ## at the beginning is new 
f1b <- function(){ 
    environment(f2b) <- environment() ## 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2b(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1b() 

(3)マクロの使用eval.parent(代替これを試してみてください(...))さらに別の方法は、f2cの本文をf1c1に効果的に注入するマクロ様構造を定義することです。ここでf2ccalls <- calls + 1行(<<-は必要ありません)を除いてf2bと同じで、本体全体の包みはeval.parent(substitute({...}))です。 f2aへの呼び出しがf2cへの呼び出しに置き換えられていることを除いて、f1cf1aと同じです。

f2c <- function(P) eval.parent(substitute({ 
    calls <- calls + 1 
    print(calls) 
    return(P + c + d) 
})) 

a <- 1 
b <- 2 
f1c <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2c(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1c() 

(4)これは、ほとんどそれはむしろそれを私たち自身を行うよりも、マクロを定義するためにgtoolsパッケージ内defmacroを使用する以外は、最後の溶液と同じであるをDEFMACRO。 (別のdefmacroバージョンのRcmdrパッケージも参照してください)。の仕組みのため、callsを渡す必要がありますが、マクロではなく関数ではないため、callsを代用するように指示しています。callsをa関数。

library(gtools) 

f2d <- defmacro(P, calls, expr = { 
    calls <- calls + 1 
    print(calls) 
    return(P + c + d) 
}) 

a <- 1 
b <- 2 
f1d <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2d(P=0, calls) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1d() 
+0

これは私が望む結果をもたらしますが、私は自分の要求をはっきりと書きませんでした。実際には、 'f2'に渡すすべての変数をインデックスすることを避けたい、つまり' env $ 'を書いていない、それで' with 'を使ってみたのです。私は 'f2'の中で外側の'呼び出し 'を変更することはできますが、現在の環境では他の変数は変更したくありません。私は質問を更新します。 – Zhenglei

+0

私は2番目のアプローチを追加しました。 –

+0

私は必要なもののように見えます。私は実際のケースでそれをテストし、後でアップデートします。助けてくれてありがとう! – Zhenglei

1

一般に、関数内で必要とされる変数は、その引数を通じて渡されるべきです。さらに、後でその値が必要な場合は、関数から戻します。これを行わないと、すぐに奇妙な結果につながる可能性があります。変数xを定義している関数が複数ある場合はどちらを使うべきですか?変数の量が多い場合は、その変数のカスタムデータ構造を作成します。それらを名前付きリストに入れます。

+0

一般的には@Paulに同意します。私は自分のコードをRパッケージにしようとしていますが、グローバル変数バインディングのような多くの警告で簡単にCRANチェックを通過することはできません。 'f2'は関数の中で定義されているので、私は別の新しい関数でそれを使いたいので、多くの繰り返しコードがあります。コピー貼りは良い選択肢ではなく、後の手順で問題を引き起こす可能性があることを認識しています。私はまた、既存のスクリプトをあまり変更したくないので、努力を減らしたいと思っています。だから、私は新しいデータ構造を定義するのではなく、環境を渡そうとしているのです。 – Zhenglei

1

指定された環境内の他の関数を再定義する関数を使用することもできます。

ここ
test_var <- "global" 

get_test_var <- function(){ 
    return(test_var) 
} 

some_function <- function(){ 
    test_var <- "local" 
    return(get_test_var()) 

} 

some_function() # Returns "global". Not what we want here... 

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

some_function2 <- function(){ 
    test_var <- "local" 
    # define function locally 
    get_test_var2 <- function(){ 
    return(test_var) 
    } 
    return(get_test_var2()) 
} 

some_function2() # Returns "local", but 'get_test_var2' can't be used in other places. 

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

add_function_to_envir <- function(my_function_name, to_envir) { 
    script_text <- capture.output(eval(parse(text = my_function_name))) 
    script_text[1] <- paste0(my_function_name, " <- ", script_text[1]) 
    eval(parse(text = script_text), envir = to_envir) 
} 

some_function3 <- function(){ 
    test_var <- "local" 
    add_function_to_envir("get_test_var", environment()) 
    return(get_test_var()) 
} 

some_function3() # Returns "local" and we can use 'get_test_var' from anywhere. 

add_function_to_envir(my_function_name, to_envir)は、機能のスクリプトを取り込み解析し、新しい環境でそれを再評価します。

注:my_function_nameの関数名は引用符で囲む必要があります。

関連する問題