R'de “<< -” (kapsam belirleme) nasıl kullanılır?


140

R girişinde kapsam belirleme hakkında okumayı bitirdim ve <<-ödevi çok merak ediyorum .

Kılavuz, <<-anladığımı hissettiğim bir (çok ilginç) örnek gösterdi . Hala eksik olduğum şey, bunun ne zaman yararlı olabileceğinin bağlamı.

Sizden okumak istediğim şey, kullanımının ne zaman <<-ilginç / yararlı olabileceğine dair örnekler (veya örneklere bağlantılar) . Kullanmanın tehlikeleri neler olabilir (izini kaybetmek kolay görünüyor) ve paylaşmak gibi hissettiğiniz ipuçları.

Yanıtlar:


196

<<-durumu korumak için kapaklarla birlikte en kullanışlıdır. İşte son zamanlardaki bir makalemden bir bölüm:

Kapatma, başka bir işlev tarafından yazılan bir işlevdir. Kapaklar , üst işlevin ortamını kapsadıkları ve bu işlevdeki tüm değişkenlere ve parametrelere erişebildikleri için çağrılır . Bu faydalıdır çünkü iki parametre seviyesine sahip olmamızı sağlar. Bir düzey parametre (üst öğe) işlevin nasıl çalıştığını kontrol eder. Diğer seviye (çocuk) işi yapar. Aşağıdaki örnek, bir güç işlevleri ailesi oluşturmak için bu fikri nasıl kullanabileceğinizi gösterir. Ana işlev ( power) , gerçekten zor işi yapan alt işlevler ( squareve ) oluşturur 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

Değişkenleri iki düzeyde yönetme özelliği, bir işlevin üst öğesinin ortamındaki değişkenleri değiştirmesine izin vererek işlev çağrıları arasında durumun korunmasını da mümkün kılar. Değişkenleri farklı seviyelerde yönetmenin anahtarı çift oklu atama operatörüdür <<-. Her <-zaman geçerli düzeyde çalışan olağan tek ok atamasının ( ) aksine, çift ​​ok operatörü üst düzeylerdeki değişkenleri değiştirebilir.

Bu, aşağıdaki örnekte gösterildiği gibi, bir işlevin kaç kez çağrıldığını kaydeden bir sayacı korumayı mümkün kılar. Her new_counterçalıştırıldığında, bir ortam oluşturur i, bu ortamdaki sayacı başlatır ve sonra yeni bir işlev oluşturur.

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

Yeni işlev bir kapak ve çevresi kapalı ortamdır. Kapaklar counter_oneve counter_twoçalıştırıldığında, her biri sayacı çevreleyen ortamda değiştirir ve geçerli sayımı döndürür.

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

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

4
Hey bu Rosettacode'daki çözülmemiş bir R görevi ( rosettacode.org/wiki/Accumulator_factory#R ) Evet, öyleydi ...
Karsten W.

1
Bir ana işleve 1'den fazla kapanmayı dahil etmeye gerek var mı? Sadece bir pasajı denedim, sadece son kapatma yapıldı gibi görünüyor ...
mckf111

"<< -" işaretine eşit bir işaret var mı?
Genom

38

<<-Eşdeğer olarak düşünmeye yardımcı olur assign( inheritsbu işlevdeki parametreyi olarak ayarlarsanız TRUE). Faydası assignben kullanmayı tercih böylece, daha fazla parametre (örneğin çevre) belirtmek için izin vermesidir assignüzerinde <<-çoğu durumda.

Kullanılması <<-ve assign(x, value, inherits=TRUE)demekse "Sağlanan ortamın ortamlarını kapsayan değişken 'x' ile karşılaşana kadar aranır." Başka bir deyişle, bu ada sahip bir değişken bulana kadar ortamlarda ilerlemeye devam edecek ve bunu ona atayacaktır. Bu, bir işlev kapsamında veya küresel ortamda olabilir.

Bu işlevlerin ne yaptığını anlamak için, R ortamlarını da (örn. Kullanma search) anlamalısınız .

Büyük bir simülasyon çalıştırırken bu işlevleri düzenli olarak kullanıyorum ve ara sonuçları kaydetmek istiyorum. Bu, nesneyi verilen işlevin veya applydöngünün kapsamı dışında oluşturmanıza olanak tanır . Bu, özellikle beklenmedik bir şekilde sona eren büyük bir döngü hakkında endişeleriniz varsa (örn. Bir veritabanı bağlantısı), bu durumda işlemdeki her şeyi kaybedebilirsiniz. Bu, sonuçlarınızı uzun bir işlem sırasında bir veritabanına veya dosyaya yazmakla eşdeğerdir, ancak sonuçları R ortamında saklar.

Bununla ilgili birincil uyarım: dikkatli olun çünkü artık özellikle değişkenlerle çalışırken global değişkenlerle çalışıyorsunuz <<-. Bu, bir işlevin parametre olarak sağlanan bir işlev kullanmasını beklediğinizde, ortamdan bir nesne değeri kullandığı durumlarla sonuçlanabileceğiniz anlamına gelir. Bu, işlevsel programlamanın kaçınmaya çalıştığı ana şeylerden biridir (bkz. Yan etkiler ). Değerlerimi işlev içinde hiç kullanılmayan benzersiz bir değişken adlarına (bir set veya benzersiz parametrelerle yapıştır kullanarak) atayarak, ancak yalnızca önbellekleme için ve daha sonra kurtarmam gerektiğinde (veya bazı metalar yapmam gerekirse) bu sorunu önlerim - ara sonuçlarda analiz).


3
Teşekkürler Tal. Gerçekten kullanmama rağmen bir blogum var. Asla bir yayını bitiremem çünkü mükemmel olmadığı sürece hiçbir şey yayınlamak istemiyorum ve bunun için zamanım yok ...
Shane

2
Bilge bir adam bir zamanlar bana mükemmel olmanın önemli olmadığını söyledi - sadece ayakta durdun - ki sen de öylesın ve mesajların da öyle olacak. Ayrıca - bazen okuyucular yorumlarla metni geliştirmeye yardımcı olur (blogumda olan şey budur). Umarım bir gün tekrar gözden
geçirirsiniz

9

Kullandığım bir yer <<-tcl / tk kullanarak basit GUI'lerde oldu. İlk örneklerden bazıları buna sahiptir - durum bilgisi için yerel ve global değişkenler arasında bir ayrım yapmanız gerektiğinden. Örneğin bakınız

 library(tcltk)
 demo(tkdensity)

kullanır <<-. Aksi takdirde Marek :) ile hemfikirim - bir Google araması yardımcı olabilir.


İlginçtir, tkdensityR 3.6.0'da bir şekilde bulamıyorum .
NelsonGon


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

11
Bu nerede iyi bir örnektir değil kullanmak <<-. Bu durumda bir for döngüsü daha net olur.
hadley

4

Bu konuda <<-operatörün bir for döngüsü içinde (yanlış) uygulandığında garip davranacağını belirtmek isterim (başka durumlar da olabilir). Aşağıdaki kod verildiğinde:

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

işlevin beklenen toplamı (6) döndürmesini bekleyebilirsiniz, ancak bunun yerine 0 döndürür, global bir değişken mySumyaratılır ve 3 değeri atanır. Burada neler olduğunu tam olarak açıklayamam ama kesinlikle bir döngü değil , yeni bir kapsam 'seviye'. Bunun yerine, R fortestfonksiyonun dışına bakar , mySumatanacak bir değişken bulamaz , bu yüzden bir tane oluşturur ve döngüde ilk kez 1 değerini atar. Müteakip iterasyonlarda, ödevdeki RHS (değişmemiş) iç mySumdeğişkeni ifade ederken, LHS genel değişkeni ifade eder. Bu nedenle her yineleme, global değişkenin değerinin yinelemenin değerinin üzerine yazıldığından iişlevden çıkışta 3 değerine sahiptir.

Umarım bu birisine yardımcı olur - bu beni bugün birkaç saatliğine boğdu! (BTW, sadece yerini <<-ile <-beklendiği gibi ve fonksiyon eserleri).


2
örneğinizde, yerel mySumhiçbir zaman artırılmaz, yalnızca küreseldir mySum. Bu nedenle for döngüsünün her yinelemesinde, global mySumdeğeri alır 0 + i. Bunu ile takip edebilirsiniz debug(fortest).
ClementWalter

Bunun for-loop olmasıyla bir ilgisi yok; iki farklı kapsamdan bahsediyorsunuz. Yalnızca <-işlevin içindeki yerel değişkeni güncellemek istiyorsanız, işlev içinde her yerde tutarlı bir şekilde kullanın .
smci

Veya << - her yerde @smci kullanın. Globallerden kaçınmak en iyisidir.
Örnek

3

<<-Operatör ayrıca için yararlı olabilir Referans Yöntemleri yazarken Referans Classes . Örneğin:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.