31

2@Gを更新します 。グロタンディークは2つのアプローチを投稿しました。2つ目は、関数内の関数環境を変更することです。これにより、コーディングの複製が多すぎるという私の問題が解決します。スクリプトをパッケージにするときに、これがCRANチェックを通過するのに適した方法かどうかはわかりません。結論が出たらまた更新します。

アップデート

私は多くの入力引数変数をに渡そうとしてf2いますが、関数内のすべての変数をとしてインデックス付けしたくないので、and (変更された)でenv$c, env$d, env$calls使用しようとしました。ただし、内部では機能しません。外部に移動すると機能しますが、実際の場合、式の内部にいくつかのsがあり、関数から簡単に移動する方法がわかりません。withf5f6f2assignwith{}assignwithassignwithwith

次に例を示します。

## 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()

関数f2は内部f1にあり、が呼び出されると、環境内のf2変数を検索します。これが私が欲しかったものです。calls,c,denvironment(f1)

ただし、他の関数でも使用したい場合はf2、代わりにグローバル環境でこの関数を定義し、と呼びます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()

安全な方法はcalls,c,d、の入力引数で定義してから、f4これらのパラメータをに渡す必要がありますf4。ただし、私の場合、この関数に渡すには変数が多すぎるためf4、環境として渡すことができf4、グローバル環境()を参照せず、 whenが呼び出されenvironment(f4)たときにのみ内部を参照するように指示する方がよいでしょう。environmentf3

私が今それを解決する方法は、環境をリストとして使用し、with関数を使用することです。

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)assign変数callsは、目的関数がである最適化関数に接続されていますf5。これが、入力引数としてassign渡す代わりに使用する理由です。calls使用する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()

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

4

4 に答える 4

31

(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)呼び出された関数の環境をリセットする次のように、 f2binの環境をリセットできf1bます。

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(substitute(...)) を使用したマクロさらに別のアプローチは、f2cインラインの本体をに効果的に注入するマクロのような構造を定義することf1c1です。これは、行(不要)とボディ全体のラッピングを除いf2cて同じです。 の呼び出しがへの呼び出しに置き換えられることを除いて、と同じです。 f2bcalls <- calls + 1<<-eval.parent(substitute({...}))f1cf1af2af2c

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)defmacrodefmacroこれは、gtoolsパッケージでマクロを自分で定義するのではなく定義するために 使用することを除いて、最後のソリューションとほぼ同じです。(別のdefmacroバージョンについてはRcmdrパッケージも参照してください。)defmacro動作方法のため、渡す必要もありますcallsが、これはマクロであり関数ではないため、これは単に置換するように指示し、関数callsに渡すことと同じではありませんcalls

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()
于 2013-01-18T14:37:30.607 に答える
2

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

于 2013-01-18T13:59:20.870 に答える
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引用符で囲む必要があります。

于 2016-06-26T15:07:20.690 に答える
0

ネストされた関数を使用し、変数を引数として渡さず、代わりに、で渡す...場合は常に、すべてのネストされた関数で次の関数を使用して、親環境から変数を取得します。

LoadVars <- function(variables, ...){
  for (var in 1:length(variables)) {
    v <- get(variables[var], envir = parent.frame(n=2))
    assign(variables[var], v, envir = parent.frame(n=1))
  }
}

入れ子関数の中で、私は次にLoadVars(c("foo", "bar"))

このアプローチは、引数を介して変数を渡す場合と同様に、必要な変数のみを渡すという意味で役立ちます。

アプローチ2

ただし、この関数を書き直して、親関数からすべての変数をロードするのは簡単です。必要に応じて、元の値である。からn値を増やすだけです。parent.frame2

LoadVars <- function(){
  variables <- ls(envir = parent.frame(n=2))

  for (var in 1:length(variables)) {
    v <- get(variables[var], envir = parent.frame(n=2))
    assign(variables[var], v, envir = parent.frame(n=1))
  }
}

a <- 1

A <- function(...){
  b <- 2
  printf("A, a = %s", a)
  printf("A, b = %s", b)
  B()
}

B <- function(...){
  LoadVars()
  printf("B, a = %s", a)
  printf("B, b = %s", b)
}

A()

に変数をロードしない場合B、それはグローバル環境変数であるためBロードできますが、にある変数はロードできません。abA()

出力:

[1] "A, a = 1"
[1] "A, b = 2"
[1] "B, a = 1"
[1] "B, b = 2"
于 2020-03-15T09:56:59.233 に答える