Rで「<<-」(スコープ指定)をどのように使用しますか?


140

Rイントロでのスコープについて読み終え​​たばかりで、その<<-割り当てについて非常に興味があります。

マニュアルには、の1つの(非常に興味深い)例が示さ<<-れていました。私がまだ足りないのは、これがいつ役立つかというコンテキストです。

だから私があなたから読んでもらいたいのは、の使用<<-が興味深い/役に立つ場合の例(または例へのリンク)です。それを使用することの危険性とは何か(簡単に追跡できなくなっているように見える)、および共有したいと思うかもしれないヒント。

回答:


196

<<-状態を維持するためにクロージャーと組み合わせると最も役立ちます。これが最近の私の論文のセクションです:

クロージャーは、別の関数によって作成された関数です。クロージャーは、親関数の環境を囲み、その関数内のすべての変数とパラメーターにアクセスできるため、そのように呼ばれます。2つのレベルのパラメーターを持つことができるため、これは便利です。パラメーターの1つのレベル(親)は、関数の動作を制御します。他のレベル(子)が作業を行います。次の例は、このアイデアを使用して力関数のファミリーを生成する方法を示しています。親関数(power)は、実際に困難な作業を行う子関数(squareおよびcube)を作成します。

power <- function(exponent) {
  function(x) x ^ exponent
}

square <- power(2)
square(2) # -> [1] 4
square(4) # -> [1] 16

cube <- power(3)
cube(2) # -> [1] 8
cube(4) # -> [1] 64

2つのレベルで変数を管理する機能により、関数がその親の環境で変数を変更できるようにすることで、関数呼び出し全体の状態を維持することもできます。さまざまなレベルで変数を管理するための鍵は、二重矢印代入演算子 <<-です。<-常に現在のレベルで機能する通常の単一矢印割り当て()とは異なり、二重矢印演算子は親レベルの変数を変更できます。

これにより、次の例に示すように、関数が呼び出された回数を記録するカウンターを維持できます。毎回new_counter実行される、環境が作成さiれ、この環境でカウンターが初期化されてから、新しい関数が作成されます。

new_counter <- function() {
  i <- 0
  function() {
    # do something useful, then ...
    i <<- i + 1
    i
  }
}

新しい関数はクロージャーであり、その環境は囲い込み環境です。クロージャcounter_onecounter_twoが実行されると、それぞれがエンクロージング環境でカウンタを変更し、現在のカウントを返します。

counter_one <- new_counter()
counter_two <- new_counter()

counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1

4
こんにちは、Rosettacode(rosettacode.org/wiki/Accumulator_factory#R)の未解決のRタスクです。まあ、それは...
Karsten W.

1
1つの親関数に複数のクロージャーを含める必要はありますか?私は1つのスニペットを試したところ、最後のクロージャのみが実行されたようです...
mckf111

「<<-」記号の代わりに等号記号はありますか?
ジェノム

38

(その関数のパラメーターをに設定した場合)<<-と同等と考えると役立ちます。の利点は、より多くのパラメーター(環境など)を指定できることです。そのため、assigninheritsTRUEassignassign<<-、ほとんどの場合。

使用する <<-assign(x, value, inherits=TRUE)、「変数 'x'が検出されるまで、指定された環境の囲まれた環境が検索されます。」つまり、その名前の変数が見つかるまで順番に環境を調べていき、その名前に割り当てます。これは、関数のスコープ内でも、グローバル環境でも可能です。

これらの関数の機能を理解するには、R環境も理解する必要があります(例: search)。

大規模なシミュレーションを実行していて、中間結果を保存したい場合は、これらの関数を定期的に使用しています。これにより、特定の関数のスコープ外でオブジェクトを作成できます。applyループの。これは、特に、大きなループが予期せず終了する(データベースの切断など)ことに懸念がある場合に非常に役立ちます。この場合、プロセスのすべてが失われる可能性があります。これは、R環境内に結果を保存することを除いて、長時間実行プロセス中にデータベースまたはファイルに結果を書き込むのと同じです。

これに関する私の主な警告:特にを使用する場合は、グローバル変数で作業しているので注意してください<<-。つまり、関数がパラメーターとして提供されたオブジェクト値を使用すると予想したときに、関数が環境からのオブジェクト値を使用している状況になる可能性があります。これは、関数型プログラミングが回避しようとする主なものの1つです(副作用を参照)。関数内で決して使用されないが、キャッシュに使用され、後で回復する必要がある場合(またはいくつかのメタを実行する場合) -中間結果の分析)。


3
Talに感謝します。私はブログを持っていますが、実際には使用していません。完璧でない限り何も公開したくないので、投稿を完成させることはできません。そのための時間がないのです...
シェーン

2
賢者はかつて私に、完璧であることは重要ではありません-立っていることだけが重要です-あなたがそうであるように、そしてあなたの投稿もそうです。また、読者がコメント付きでテキストを改善するのを助けることもあります(それが私のブログで起こっていることです)。私はいつかあなたが再考することを望みます:)
タル・ガリリ

9

私が使用し<<-た場所の1つは、tcl / tkを使用した単純なGUIでした。最初の例にはそれがあります-ステートフルネスのためにローカル変数とグローバル変数を区別する必要があるためです。例を見る

 library(tcltk)
 demo(tkdensity)

を使用し<<-ます。それ以外の場合は、マレクに同意します:)-Google検索が役立ちます。


興味深いことに、tkdensityR 3.6.0 ではどういうわけか見つかりません。
NelsonGon


5
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")

11
これは、使用しない場所の良い例です<<-。この場合、forループはより明確になります。
ハドリー

4

この問題について<<-、forループ内で(誤って)適用されると、オペレーターが奇妙な動作をすることを指摘しておきます(他のケースもある可能性があります)。次のコードがあるとします:

fortest <- function() {
    mySum <- 0
    for (i in c(1, 2, 3)) {
        mySum <<- mySum + i
    }
    mySum
}

関数が期待される合計6を返すと期待するかもしれませんが、代わりに0を返します。グローバル変数mySumが作成され、値3が割り当てられます。ループは新しいスコープ「レベル」ではありません。代わりに、Rはfortest関数の外を見mySumて、割り当てる変数を見つけることができないようです。そのため、最初にループを介して、変数を作成し、値1を割り当てます。後続の反復では、割り当てのRHSは(変更されていない)内部mySum変数を参照している必要がありますが、LHSはグローバル変数を参照しています。したがって、各反復はグローバル変数の値をその反復の値に上書きするためi、関数の終了時には値3になります。

これが誰かを助けることを願っています-これは今日数時間私を困らせました!(ちなみに、<<-<-、関数は期待どおりに動作します)。


2
あなたの例では、ローカルmySumは増分されず、グローバルのみが増分されますmySum。したがって、forループの各反復で、グローバルmySumは値を取得します0 + i。これはでフォローできますdebug(fortest)
ClementWalter 2015年

forループであることとは関係ありません。2つの異なるスコープを参照しています。<-関数内のローカル変数のみを更新する場合は、関数内のどこでも一貫して使用してください。
smci 2016

または、<<-everywhere @smciを使用します。グローバルを回避するのが最善ですが。
例による統計の学習2017

3

この<<-演算子は、参照メソッドを記述するとき参照クラスにも役立ちます。例えば:

myRFclass <- setRefClass(Class = "RF",
                         fields = list(A = "numeric",
                                       B = "numeric",
                                       C = function() A + B))
myRFclass$methods(show = function() cat("A =", A, "B =", B, "C =",C))
myRFclass$methods(changeA = function() A <<- A*B) # note the <<-
obj1 <- myRFclass(A = 2, B = 3)
obj1
# A = 2 B = 3 C = 5
obj1$changeA()
obj1
# A = 6 B = 3 C = 9
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.