Saya berlatih menggunakan objek tidak berubah dalam C ++. Tujuan pribadi saya mewakili grafik objek generik (dalam tumpukan) dengan urutan grafik yang tidak berubah.
Membangun grafik multi-versi itu sendiri tidak terlalu sulit. Masalahnya adalah kinerja. Versi brute-force membutuhkan salinan grafik lengkap, dan ini tidak dapat diterima.
Saya mencoba berbagi node yang tidak berubah. Tetapi dalam kasus ini, saya mendapat masalah baru; referensi. Referensi ke objek lain harus diperbarui dalam grafik keseluruhan. Ini perlu mengunjungi semua node untuk setiap kali saya mendapatkan versi grafik baru. Dan ini bermutasi node dengan referensi, sehingga mereka juga harus diturunkan (dengan menyalin). Performa tidak akan lebih baik daripada menyalin dengan kasar.
Sejauh yang dapat saya bayangkan, tidak ada cara yang sangat efisien untuk mewakili mutasi grafik objek dengan keadaan tidak berubah. Jadi saya meminta ide tentang ini.
Apakah mungkin untuk merepresentasikan mutasi grafik objek secara efisien dengan keadaan tidak berubah?
sumber
Jawaban:
Apa yang Anda cari disebut Struktur Data Persisten . Sumber daya kanonik untuk struktur data persisten adalah Buku Struktur Data Murni Fungsional karya Chris Okasaki . Struktur data yang gigih telah mengumpulkan minat dalam beberapa waktu terakhir karena popularisasi mereka di Clojure dan Scala.
Namun, untuk beberapa alasan aneh, Grafik Persisten nampaknya sebagian besar diabaikan. Kami memiliki daftar, lusinan jenis pohon, susunan, antrian prioritas, peta, tetapi tidak ada grafik.
Saya benar-benar hanya menemukan satu kertas: Grafik Sepenuhnya Persisten - Yang Satu Untuk Pilih?
sumber
Jika Anda tidak menganggap koneksi antara objek menjadi bagian dari sumber daya versi Anda (dan Anda mungkin - dalam hal ini mungkin tidak banyak membantu), Anda dapat mempertimbangkan memecah objek Anda menjadi bagian yang mewakili bagian konektivitas dari objek dan bagian yang mewakili keadaan tidak berubah.
Kemudian Anda bisa memiliki masing-masing sub-objek konektivitas yang mengandung vektor dari status yang diversi. Dengan cara ini Anda dapat beroperasi dengan nomor versi grafik untuk mengakses keadaan tidak berubah yang sesuai.
Untuk menghindari harus melintasi seluruh grafik setiap kali ada pembaruan ke node tertentu, Anda dapat membuatnya sehingga jika sebuah node diakses dengan nomor versi daripada lebih besar dari nomor versi node saat ini, versi saat ini digunakan . Jika node kemudian diperbarui, Anda mengisi semua versi menengah dengan versi sebelumnya - sehingga memungkinkan Anda untuk melakukan pembaruan malas ke grafik objek.
Jika konektivitas antara objek adalah bagian dari negara versi Anda, maka hal di atas tidak berfungsi. Tapi mungkin Anda bisa memperluasnya sebagai berikut:
Untuk setiap objek dalam grafik, buat "handle objek". Objek pegangan berisi daftar status abadi yang diversi. Daripada menyimpan referensi objek di salah satu objek grafik, Anda akan menyimpan referensi ke objek handle. Kemudian setiap referensi ke objek akan dide-referensi melalui pegangan menggunakan pegangan dan nomor versi tampilan grafik objek yang saat ini sedang diproses. Ini akan mengembalikan keadaan tidak berubah yang benar untuk objek. Status tidak berubah menggunakan pegangan untuk merujuk ke objek lain dalam grafik, sehingga Anda selalu mendapatkan tanggal yang konsisten untuk versi grafik yang ingin Anda proses.
Logika yang dijelaskan di atas berlaku untuk memperbarui versi di dalam pegangan - yang memungkinkan pembaruan yang malas dan terlokalisasi.
sumber
Ada solusi yang diterbitkan untuk masalah ini dengan kompleksitas waktu diamortisasi yang sangat baik, tetapi sulit ditemukan ketika Anda tidak tahu persis apa yang harus dicari. Anda dapat menemukan ringkasan yang bagus dalam catatan kuliah ini (periksa bagian 2.2.3) atau baca makalah asli Membuat Struktur Data Tetap Bertahan . Jika grafik objek Anda memiliki konektivitas terbatas (misalnya struktur mirip pohon), Anda bahkan dapat mencapai kompleksitas diamortisasi O (1) untuk membaca dan memperbarui grafik yang tidak dapat diubah, yang mengesankan.
Idenya adalah bahwa setiap objek, selain menyimpan keadaannya yang tidak berubah saat ini, menyediakan ruang untuk merekam perubahan. Saat Anda ingin membuat grafik abadi dari yang sudah ada dengan menerapkan perubahan, Anda harus mempertimbangkan dua kasus:
Objek yang ingin Anda ubah masih memiliki ruang untuk perubahan. Anda dapat menyimpan perubahan secara langsung di objek.
Objek tidak memiliki lebih banyak ruang untuk perubahan. Anda membuat instance baru berdasarkan nilai saat ini dan mengosongkan catatan perubahan. Sekarang Anda perlu memperbarui secara rekursif semua objek referensi objek lama untuk referensi objek baru.
Jika objek referensi itu sendiri masih memiliki ruang untuk perubahan, Anda dapat menyimpan perubahan dalam referensi secara langsung, jika tidak, ia akan mengalir secara rekursif.
Sementara skema ini mendukung pembuatan efisien dari generasi baru dari grafik objek yang tidak dapat diubah itu mempersulit pembacaannya, karena sekarang Anda perlu menentukan "versi" mana yang ingin Anda akses ketika membaca data dari objek yang tidak dapat diubah. Ini karena objek yang tidak dapat diubah mungkin memiliki informasi untuk beberapa versi karena catatan perubahan yang disimpan, jadi Anda perlu menentukan versi mana yang ingin Anda lihat.
Saat menerapkan ini saya mencoba menyembunyikan kompleksitas ini di API. Ketika mereferensikan sesuatu dalam grafik yang tidak dapat diubah dari luar, saya menggunakan tulisan rintisan yang dihasilkan alih-alih referensi langsung, jadi penelepon tidak perlu terus melewati versi yang diinginkan secara manual. Ini membuat referensi sedikit lebih mahal daripada pointer langsung, tetapi saya merasa sepadan dengan kenyamanan.
sumber