R6: Kloning kelas dengan objek yang saling mereferensi

Dibuat pada 7 Feb 2019  ·  4Komentar  ·  Sumber: r-lib/R6

Saya sudah mengomentari ini di #110, tapi saya ingin mendapatkan pendapat "resmi" tentang ini (dan saya bisa membuat PR jika ini sesuatu yang diinginkan). Saat ini sangat sulit untuk clone(deep = TRUE) objek yang memiliki objek anggota yang saling mereferensikan.

Contohnya adalah sebagai berikut: r2 berisi objek y , yang mereferensikan objek x . Mengubah x melalui referensi di y dimungkinkan di objek asli obj1 , tetapi bukan klon obj2 :

rr = R6::R6Class("test",
  public = list(
    ref = NULL,
    val = 0,
    initialize = function(ref) {
      self$ref <- ref
    }
  )
)

r2 = R6::R6Class("test",
  public = list(
    x = NULL,
    y = NULL,
    initialize = function() {
      self$x <- rr$new(NULL)
      self$y <- rr$new(self$x)
    }
  )
)

obj1 <- r2$new()
obj2 <- obj1$clone(deep = TRUE)

obj1$y$ref$val <- 100
print(obj1$x$val)  # prints '100', as it should

obj2$y$ref$val <- 200
print(obj2$x$val)  # prints '0'

Solusi telah diposting oleh @dfalbel , tetapi ada dua kelemahan untuk ini: (1) menimpa metode clone sepenuhnya berarti harus membangun objek dari awal, dan (2) metode ini kurang didukung dengan baik dan tampaknya benar-benar mengatasi batasan yang ada di R6::R6Class yang tampaknya mencoba mencegah metode clone kustom.

Akan berguna untuk memiliki opsi untuk membiarkan beberapa kode dieksekusi setelah sebagian besar pekerjaan clone() selesai, untuk melakukan beberapa pasca-pemrosesan pada objek kloning. Dalam contoh di atas maka akan memungkinkan untuk memperbaiki referensi di objek y$ref , misalnya. API yang saya sarankan adalah memberi pengguna opsi untuk mendefinisikan metode private$post_clone(old_self) , yang dipanggil di akhir clone() . Perubahan yang diperlukan mungkin akan memasukkan garis

if (deep && has_private && is.function(new[[1]]$private$post_clone)) {
  new[[1]]$private$post_clone(old_1_binding)
}
new_1_binding

di akhir dia mengkloning fungsi .

Saya akan tertarik mendengar pendapat Anda tentang ini.

feature

Komentar yang paling membantu

Saya suka ide metode post_clone() . Ide lain, seperti yang disarankan di #110, adalah mengganti nama metode clone ini menjadi .clone , dan kemudian mendefinisikan clone = function() self$.clone() secara default dan mengizinkan pengguna untuk menimpanya.

Satu keuntungan yang post_clone() akan memiliki lebih dari .clone() adalah, karena dijalankan dari objek baru, itu akan memungkinkan akses ke anggota pribadi objek baru; metode .clone , karena dijalankan dari objek lama, hanya akan mengizinkan akses ke anggota publik objek baru. Tetapi saya dapat membayangkan bahwa pada objek aslinya, Anda ingin melakukan beberapa hal sebelum dan/atau pasca kloning. Jadi mungkin masuk akal untuk melakukan keduanya.

Ini juga akan memungkinkan pengguna untuk membuat deep_clone=TRUE default untuk kelas.

Saya pikir ketika harus mengganti metode klon bawaan sepenuhnya dengan versi khusus (solusi yang disarankan di #110), pada dasarnya tidak mungkin mengharapkan penulis kelas untuk melakukannya dengan benar dalam kasus umum, jadi itu akan menjadi baik untuk memungkinkan mereka memanggil kode kloning bawaan R6 dan kemudian memijat hasilnya sesudahnya. (Mendapatkan kloning yang tepat untuk kelas sederhana tidak terlalu sulit, tetapi ketika pewarisan dan pengikatan aktif ikut bermain, itu menjadi sangat, sangat sulit. Saat ini hampir 1000 baris kode untuk tes kloning .)

Semua 4 komentar

Saya suka ide metode post_clone() . Ide lain, seperti yang disarankan di #110, adalah mengganti nama metode clone ini menjadi .clone , dan kemudian mendefinisikan clone = function() self$.clone() secara default dan mengizinkan pengguna untuk menimpanya.

Satu keuntungan yang post_clone() akan memiliki lebih dari .clone() adalah, karena dijalankan dari objek baru, itu akan memungkinkan akses ke anggota pribadi objek baru; metode .clone , karena dijalankan dari objek lama, hanya akan mengizinkan akses ke anggota publik objek baru. Tetapi saya dapat membayangkan bahwa pada objek aslinya, Anda ingin melakukan beberapa hal sebelum dan/atau pasca kloning. Jadi mungkin masuk akal untuk melakukan keduanya.

Ini juga akan memungkinkan pengguna untuk membuat deep_clone=TRUE default untuk kelas.

Saya pikir ketika harus mengganti metode klon bawaan sepenuhnya dengan versi khusus (solusi yang disarankan di #110), pada dasarnya tidak mungkin mengharapkan penulis kelas untuk melakukannya dengan benar dalam kasus umum, jadi itu akan menjadi baik untuk memungkinkan mereka memanggil kode kloning bawaan R6 dan kemudian memijat hasilnya sesudahnya. (Mendapatkan kloning yang tepat untuk kelas sederhana tidak terlalu sulit, tetapi ketika pewarisan dan pengikatan aktif ikut bermain, itu menjadi sangat, sangat sulit. Saat ini hampir 1000 baris kode untuk tes kloning .)

Saya kebanyakan menggunakan R6 untuk membungkus kelas C++. Dalam kasus penggunaan saya, saya memiliki self$pointer dan semua metode mengirimkan pointer ini ke cpp membuat calcs. Saat mengkloning kelas ini, saya hanya perlu membuat objek baru dan pointer baru di sisi cpp dan kemudian membuat objek Class$new(cpp_ptr) . Dalam hal ini tampaknya aman untuk mengganti perilaku metode klon.

Juga, tidak mengubah perilaku clone sini adalah salah. Menggunakan self$clone tidak akan mengkloning kelas sama sekali.

Saya suka ide post_clone dan saya pikir itu mencakup sebagian besar kasus penggunaan, tetapi IMHO metode .clone akan menjadi pendekatan yang lebih baik.

Saya juga sangat menyukai ide metode post_clone atau .clone . Saya memiliki masalah pegeler/eddington2#4 yang hampir identik dengan #178.

Saat ini saya telah menetapkan cloneable = FALSE dan berencana untuk _try_ membuat metode klon khusus. Namun, seperti yang ditunjukkan @wch , ini kemungkinan akan menjadi sangat sulit mengingat cara saya mengatur kelas saya sejauh ini. Untungnya, dengan beberapa modifikasi pada pendekatan saya, saya dapat mengambil beberapa jalan pintas untuk mempermudah segalanya. :senyum:

Apakah ada pemikiran lagi yang diberikan untuk mengimplementasikan salah satu metode ini dalam rilis paket ini di masa mendatang?

Banyak contoh tantangan kloning dengan objek R6 bersarang telah menggunakan siklus referensi o1 -> o2 -> o1 relatif sederhana (misalnya solusi copy disediakan di #110). Saya ingin tahu apakah ada percakapan/perencanaan (atau selera sama sekali) untuk mencoba mengatasi masalah siklus yang lebih umum dalam grafik referensi saat kloning dalam (yaitu o1 -> o2 -> o3 -> ... -> o1) . Dalam bahasa berbasis referensi lainnya, paling sering operasi kloning ini memerlukan beberapa manajemen status _selama_ proses kloning rekursif (yaitu status dibagikan melalui tumpukan panggilan rekursif).

Apakah metode tersebut telah didiskusikan (atau sedang dikerjakan) untuk kelas R6?
Saya sedang mempertimbangkan untuk mengimplementasikan versi saya sendiri (sangat disederhanakan & spesifik) untuk beberapa kasus penggunaan saya, jadi saya bertanya-tanya apakah saya harus (i) mungkin hanya menunggu, (ii) bergabung/mendengarkan yang sedang berlangsung percakapan tentang upaya serupa, atau (iii) pertimbangkan untuk menggeneralisasi solusi saya sedikit lebih banyak untuk dibagikan kepada orang lain.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

wch picture wch  ·  8Komentar

mattwarkentin picture mattwarkentin  ·  7Komentar

rappster picture rappster  ·  11Komentar

rappster picture rappster  ·  3Komentar

kenarab picture kenarab  ·  8Komentar