163

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

マニュアルには、の1つの(非常に興味深い)例が示されていまし<<-たが、私はそれを理解したと感じています。私がまだ見逃しているのは、これがいつ役立つかという文脈です。

ですから、私があなたから読みたいのは、いつの使用<<-が面白く/役立つかについての例(または例へのリンク)です。それを使用することの危険性(追跡を失うのは簡単に見える)と、共有したいと思うかもしれないヒントは何ですか?

4

6 に答える 6

219

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

クロージャは、別の関数によって記述された関数です。クロージャは、親関数の環境を囲み、その関数のすべての変数とパラメーターにアクセスできるため、いわゆるクロージャです。これは、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
于 2010-04-13T14:18:18.157 に答える
43

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

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

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

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

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

于 2010-04-13T12:34:26.947 に答える
9

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

 library(tcltk)
 demo(tkdensity)

を使用します<<-。そうでなければ私はマレクに同意します:)-グーグル検索が助けになります。

于 2010-04-13T12:12:30.843 に答える
6

<<-この件に関して、forループ内で(誤って)適用されると、演算子が奇妙に動作することを指摘したいと思います(他の場合もあるかもしれません)。次のコードが与えられます:

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

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

これが誰かを助けることを願っています-これは今日数時間私を困惑させました!(ところで、に置き換えるだけ<<-<-、関数は期待どおりに機能します)。

于 2015-06-15T14:58:15.187 に答える
5
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")
于 2010-04-13T12:23:22.210 に答える
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
于 2015-06-15T15:12:24.320 に答える