Bagaimana Anda menggunakan "<< -" (penugasan pelingkupan) dalam R?

140

Saya baru saja selesai membaca tentang pelingkupan di intro R , dan saya sangat ingin tahu tentang <<-tugas itu.

Manual menunjukkan satu contoh (sangat menarik) untuk <<-, yang saya rasa saya mengerti. Apa yang masih saya lewatkan adalah konteks kapan ini bisa bermanfaat.

Jadi yang ingin saya baca dari Anda adalah contoh (atau tautan ke contoh) tentang kapan penggunaan <<-bisa menarik / bermanfaat. Apa yang mungkin menjadi bahaya menggunakannya (kelihatannya mudah dilupakan), dan kiat apa pun yang mungkin ingin Anda bagikan.

Tal Galili
sumber

Jawaban:

196

<<-paling berguna dalam hubungannya dengan penutupan untuk mempertahankan keadaan. Berikut adalah bagian dari makalah saya terbaru:

Penutupan adalah fungsi yang ditulis oleh fungsi lain. Penutupan disebut demikian karena menutup lingkungan fungsi induk, dan dapat mengakses semua variabel dan parameter dalam fungsi itu. Ini berguna karena memungkinkan kita untuk memiliki dua level parameter. Satu tingkat parameter (induk) mengontrol cara kerja fungsi. Tingkat lain (anak) melakukan pekerjaan. Contoh berikut menunjukkan bagaimana bisa menggunakan ide ini untuk menghasilkan keluarga fungsi kekuasaan. Fungsi induk ( power) membuat fungsi anak ( squaredan cube) yang benar-benar melakukan kerja keras.

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

Kemampuan untuk mengelola variabel pada dua level juga memungkinkan untuk mempertahankan status di seluruh pemanggilan fungsi dengan memungkinkan fungsi untuk memodifikasi variabel di lingkungan induknya. Kunci untuk mengelola variabel di berbagai tingkat adalah operator penugasan panah ganda <<-. Tidak seperti penugasan panah tunggal biasa ( <-) yang selalu berfungsi pada level saat ini, operator panah ganda dapat memodifikasi variabel di level induk.

Ini memungkinkan untuk mempertahankan penghitung yang mencatat berapa kali suatu fungsi telah dipanggil, seperti yang ditunjukkan contoh berikut. Setiap kali new_counterdijalankan, ia menciptakan lingkungan, menginisialisasi penghitung idi lingkungan ini, dan kemudian menciptakan fungsi baru.

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

Fungsi baru adalah penutup, dan lingkungannya adalah lingkungan tertutup. Ketika penutupan counter_onedan counter_twodijalankan, masing-masing memodifikasi penghitung di lingkungan terlampir dan kemudian mengembalikan hitungan saat ini.

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

counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
Hadley
sumber
4
Hai ini adalah tugas R yang belum terpecahkan pada Rosettacode ( rosettacode.org/wiki/Accumulator_factory#R ) Baiklah, itu ...
Karsten W.
1
Apakah ada kebutuhan untuk melampirkan lebih dari 1 penutupan dalam satu fungsi induk? Saya hanya mencoba satu cuplikan, sepertinya hanya penutupan terakhir yang dieksekusi ...
mckf111
Apakah ada alternatif tanda sama dengan tanda "<< -"?
Genom
38

Akan membantu jika <<-dianggap setara dengan assign(jika Anda mengatur inheritsparameter pada fungsi tersebut TRUE). Keuntungannya assignadalah memungkinkan Anda untuk menentukan lebih banyak parameter (misalnya lingkungan), jadi saya lebih suka menggunakan assignlebih <<-banyak kasus.

Menggunakan <<-dan assign(x, value, inherits=TRUE)berarti bahwa "melampirkan lingkungan dari lingkungan yang disediakan dicari sampai variabel 'x' ditemukan." Dengan kata lain, ia akan terus melalui lingkungan dalam urutan sampai menemukan variabel dengan nama itu, dan akan menugaskannya untuk itu. Ini bisa berada dalam lingkup fungsi, atau di lingkungan global.

Untuk memahami apa yang dilakukan fungsi-fungsi ini, Anda juga harus memahami lingkungan R (misalnya menggunakan search).

Saya secara teratur menggunakan fungsi-fungsi ini ketika saya menjalankan simulasi besar dan saya ingin menyimpan hasil antara. Ini memungkinkan Anda untuk membuat objek di luar lingkup fungsi atau applyloop yang diberikan . Itu sangat membantu, terutama jika Anda memiliki kekhawatiran tentang loop besar yang berakhir secara tidak terduga (misalnya pemutusan basis data), dalam hal ini Anda bisa kehilangan segalanya dalam proses tersebut. Ini akan sama dengan menulis hasil Anda ke database atau file selama proses yang berjalan lama, kecuali bahwa itu menyimpan hasil dalam lingkungan R sebagai gantinya.

Peringatan utama saya dengan ini: hati-hati karena Anda sekarang bekerja dengan variabel global, terutama saat menggunakan <<-. Itu berarti bahwa Anda bisa berakhir dengan situasi di mana suatu fungsi menggunakan nilai objek dari lingkungan, ketika Anda mengharapkannya menggunakan yang diberikan sebagai parameter. Ini adalah salah satu hal utama yang coba dihindari oleh pemrograman fungsional (lihat efek samping ). Saya menghindari masalah ini dengan menetapkan nilai saya ke nama variabel unik (menggunakan tempel dengan set atau parameter unik) yang tidak pernah digunakan dalam fungsi, tetapi hanya digunakan untuk caching dan jika saya perlu memulihkan nanti (atau melakukan beberapa meta -Analisis pada hasil antara).

Shane
sumber
3
Terima kasih, Tal. Saya punya blog, meskipun saya tidak benar-benar menggunakannya. Saya tidak pernah bisa menyelesaikan posting karena saya tidak ingin mempublikasikan apa pun kecuali itu sempurna, dan saya tidak punya waktu untuk itu ...
Shane
2
Seorang pria bijak pernah berkata kepada saya bahwa tidak penting untuk menjadi sempurna - hanya berdiri keluar - yang Anda, dan begitu juga dengan posting Anda. Juga - terkadang pembaca membantu meningkatkan teks dengan komentar (itulah yang terjadi dengan blog saya). Saya harap suatu hari Anda akan mempertimbangkan kembali :)
Tal Galili
9

Satu tempat di mana saya menggunakan <<-GUI sederhana menggunakan tcl / tk. Beberapa contoh awal memilikinya - karena Anda perlu membuat perbedaan antara variabel lokal dan global untuk statefullness. Lihat misalnya

 library(tcltk)
 demo(tkdensity)

yang menggunakan <<-. Kalau tidak, saya setuju dengan Marek :) - pencarian Google dapat membantu.

Dirk Eddelbuettel
sumber
Menarik, saya entah bagaimana tidak dapat menemukannya tkdensitydi R 3.6.0.
NelsonGon
1
Paket tcltk dikirimkan bersama R: github.com/wch/r-source/blob/trunk/src/library/tcltk/demo/…
Dirk Eddelbuettel
5
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")
lcgong
sumber
11
Ini adalah contoh yang baik dari mana tidak boleh digunakan <<-. A for loop akan lebih jelas dalam hal ini.
Hadley
4

Mengenai hal ini saya ingin menunjukkan bahwa <<-operator akan berperilaku aneh ketika diterapkan (salah) dalam for loop (mungkin ada kasus lain juga). Diberikan kode berikut:

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

Anda mungkin berharap bahwa fungsi akan mengembalikan jumlah yang diharapkan, 6, tetapi sebaliknya mengembalikan 0, dengan variabel global mySumyang dibuat dan diberi nilai 3. Saya tidak bisa sepenuhnya menjelaskan apa yang sedang terjadi di sini, tetapi tentu saja tubuh for loop bukan 'level' lingkup baru. Sebaliknya, tampaknya R terlihat di luar fortestfungsi, tidak dapat menemukan mySumvariabel untuk ditetapkan, jadi buat satu dan tetapkan nilai 1, pertama kali melalui loop. Pada iterasi berikutnya, RHS dalam penugasan harus mengacu pada mySumvariabel dalam (tidak berubah) sedangkan LHS mengacu pada variabel global. Oleh karena itu setiap iterasi menimpa nilai variabel global ke nilai iterasi i, sehingga memiliki nilai 3 saat keluar dari fungsi.

Semoga ini bisa membantu seseorang - ini membuat saya bingung selama beberapa jam hari ini! (BTW, ganti saja <<-dengan <-dan fungsinya berfungsi seperti yang diharapkan).

Matthew Wise
sumber
2
dalam contoh Anda, lokal mySumtidak pernah bertambah tetapi hanya global mySum. Oleh karena itu pada setiap iterasi for for, global mySummendapatkan nilainya 0 + i. Anda dapat mengikuti ini dengan debug(fortest).
ClementWalter
Itu tidak ada hubungannya dengan itu menjadi for-loop; Anda mereferensikan dua cakupan yang berbeda. Cukup gunakan di <-mana saja secara konsisten dalam fungsi jika Anda hanya ingin memperbarui variabel lokal di dalam fungsi.
smci
Atau gunakan << - di mana-mana @smci. Padahal yang terbaik untuk menghindari global.
Belajar statistik dengan contoh
3

The <<-Operator juga dapat berguna untuk Kelas Referensi ketika menulis Metode Referensi . Sebagai contoh:

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
Carlos Cinelli
sumber