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.
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.
Komentar yang paling membantu
Saya suka ide metode
post_clone()
. Ide lain, seperti yang disarankan di #110, adalah mengganti nama metodeclone
ini menjadi.clone
, dan kemudian mendefinisikanclone = 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 .)