web-dev-qa-db-ja.com

スコープを念頭に置いて、Rで関数を書く

私は自分の環境で他のオブジェクトを見る必要がある関数を書くことがよくあります。例えば:

> a <- 3
> b <- 3
> x <- 1:5
> fn1 <- function(x,a,b) a+b+x
> fn2 <- function(x) a+b+x
> fn1(x,a,b)
[1]  7  8  9 10 11
> fn2(x)
[1]  7  8  9 10 11

予想どおり、fn2は実行時にaとbを「見る」ことができるため、これらの関数はどちらも同じです。しかし、これを利用し始めると、約30分以内に、必要な変数(aまたはbなど)の1つなしで関数を呼び出すことになります。これを利用しないと、不必要に物を回しているような気がします。

関数が何を必要とするかについて明確にする方が良いですか?または、インラインコメントまたは関数の他のドキュメントを介してこれを処理する必要がありますか?もっと良い方法はありますか?

30

いくつかの値によってパラメーター化され、繰り返し呼び出される関数が必要になることがわかっている場合は、クロージャーを使用してグローバルを回避します。

make.fn2 <- function(a, b) {
    fn2 <- function(x) {
        return( x + a + b )
    }
    return( fn2 )
}

a <- 2; b <- 3
fn2.1 <- make.fn2(a, b)
fn2.1(3)    # 8
fn2.1(4)    # 9

a <- 4
fn2.2 <- make.fn2(a, b)
fn2.2(3)    # 10
fn2.1(3)    # 8

これにより、グローバル変数の参照が適切に回避され、代わりにaとbの関数の囲み環境が使用されます。グローバルaおよびbを変更しても、fn2インスタンスが呼び出されたときに意図しない副作用が発生することはありません。

36
ars

一部の言語ではグローバル変数が許可されていないのには理由があります。グローバル変数はコードの破損につながる可能性があります。

Rのスコープ規則を使用すると、怠惰な方法でコードを記述できます。関数で他の環境で変数を使用できるようにすると、入力を節約でき、単純なケースで遊ぶのに最適です。

ただし、リモートで複雑なことをしている場合は、必要なすべての変数を関数に渡すことをお勧めします(または、少なくとも、変数が存在しない場合にフォールバックするために、完全なサニティチェックを実施します)。 。

上記の例では:

ベストプラクティスは、fn1を使用することです。

または、次のようなものを試してください

 fn3 <- function(x)
   {
      if(!exists("a", envir=.GlobalEnv))
      {
         warning("Variable 'a' does not exist in the global environment")
         a <- 1
      }

      if(!exists("b", envir=.GlobalEnv))
      {
         warning("Variable 'b' does not exist in the global environment")
         b <- 2
      }

      x + a + b
   }
8
Richie Cotton

関数でグローバル変数を使用しているとき、または変数を割り当てようとしているときに問題が発生しますか?後者の場合は、関数内の代入として<<-を使用していないためだと思います。そして、<<-を使用することは暗い面のように見えますが、 1 それはあなたの目的のために非常にうまくいくかもしれません。前者の場合、関数はおそらくグローバル変数をマスクしています。

ローカルでマスクするのが難しい方法でグローバル変数に名前を付けると役立つ場合があります。例:global.pimultiples <- 1:4*pi

3
Tyler

グローバル変数の使用は、ほとんどの言語で一般的に推奨されておらず、Rも例外ではありません。非常に多くの場合、短い関数は、グローバル環境に入力できる短い汎用変数名を使用します。 a)関数定義にすべての変数を含めるb)notデフォルト値を割り当てるのが最も安全です。たとえば、f = function(a = 0、b = NA)ではなくf = function(a、b)と記述します。

0
gappy