回答:
<<-
状態を維持するためにクロージャーと組み合わせると最も役立ちます。これが最近の私の論文のセクションです:
クロージャーは、別の関数によって作成された関数です。クロージャーは、親関数の環境を囲み、その関数内のすべての変数とパラメーターにアクセスできるため、そのように呼ばれます。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_one
とcounter_two
が実行されると、それぞれがエンクロージング環境でカウンタを変更し、現在のカウントを返します。
counter_one <- new_counter()
counter_two <- new_counter()
counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
(その関数のパラメーターをに設定した場合)<<-
と同等と考えると役立ちます。の利点は、より多くのパラメーター(環境など)を指定できることです。そのため、assign
inherits
TRUE
assign
assign
<<-
、ほとんどの場合。
使用する <<-
とassign(x, value, inherits=TRUE)
、「変数 'x'が検出されるまで、指定された環境の囲まれた環境が検索されます。」つまり、その名前の変数が見つかるまで順番に環境を調べていき、その名前に割り当てます。これは、関数のスコープ内でも、グローバル環境でも可能です。
これらの関数の機能を理解するには、R環境も理解する必要があります(例: search
)。
大規模なシミュレーションを実行していて、中間結果を保存したい場合は、これらの関数を定期的に使用しています。これにより、特定の関数のスコープ外でオブジェクトを作成できます。apply
ループの。これは、特に、大きなループが予期せず終了する(データベースの切断など)ことに懸念がある場合に非常に役立ちます。この場合、プロセスのすべてが失われる可能性があります。これは、R環境内に結果を保存することを除いて、長時間実行プロセス中にデータベースまたはファイルに結果を書き込むのと同じです。
これに関する私の主な警告:特にを使用する場合は、グローバル変数で作業しているので注意してください<<-
。つまり、関数がパラメーターとして提供されたオブジェクト値を使用すると予想したときに、関数が環境からのオブジェクト値を使用している状況になる可能性があります。これは、関数型プログラミングが回避しようとする主なものの1つです(副作用を参照)。関数内で決して使用されないが、キャッシュに使用され、後で回復する必要がある場合(またはいくつかのメタを実行する場合) -中間結果の分析)。
私が使用し<<-
た場所の1つは、tcl / tkを使用した単純なGUIでした。最初の例にはそれがあります-ステートフルネスのためにローカル変数とグローバル変数を区別する必要があるためです。例を見る
library(tcltk)
demo(tkdensity)
を使用し<<-
ます。それ以外の場合は、マレクに同意します:)-Google検索が役立ちます。
tkdensity
R 3.6.0 ではどういうわけか見つかりません。
この問題について<<-
、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になります。
これが誰かを助けることを願っています-これは今日数時間私を困らせました!(ちなみに、<<-
て<-
、関数は期待どおりに動作します)。
mySum
は増分されず、グローバルのみが増分されますmySum
。したがって、forループの各反復で、グローバルmySum
は値を取得します0 + i
。これはでフォローできますdebug(fortest)
。
<-
関数内のローカル変数のみを更新する場合は、関数内のどこでも一貫して使用してください。
この<<-
演算子は、参照メソッドを記述するときに参照クラスにも役立ちます。例えば:
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