PEMBARUAN: Pertanyaan ini adalah subjek blog saya pada tanggal 8 Juni 2012 . Terima kasih atas pertanyaan bagusnya!
Pertanyaan bagus. Kami memperdebatkan masalah yang Anda angkat untuk waktu yang sangat lama.
Kami ingin memiliki struktur data yang memiliki karakteristik sebagai berikut:
- Kekal.
- Bentuk pohon.
- Akses murah ke node induk dari node anak.
- Dimungkinkan untuk memetakan dari simpul di pohon ke offset karakter dalam teks.
- Gigih .
Yang saya maksud dengan ketekunan adalah kemampuan untuk menggunakan kembali sebagian besar node yang ada di pohon saat pengeditan dilakukan pada buffer teks. Karena node tidak dapat diubah, tidak ada penghalang untuk menggunakannya kembali. Kami membutuhkan ini untuk kinerja; kami tidak dapat mengurai ulang gumpalan besar file setiap kali Anda menekan tombol. Kita perlu melakukan lex ulang dan mengurai ulang hanya bagian dari pohon yang terpengaruh oleh edit.
Sekarang ketika Anda mencoba memasukkan kelima hal itu ke dalam satu struktur data, Anda langsung mengalami masalah:
- Bagaimana Anda membangun node di tempat pertama? Orang tua dan anak keduanya merujuk satu sama lain, dan tidak dapat diubah, jadi mana yang dibangun lebih dulu?
- Seandainya Anda berhasil memecahkan masalah itu: bagaimana Anda membuatnya terus-menerus? Anda tidak dapat menggunakan kembali simpul anak di induk yang berbeda karena itu akan melibatkan memberi tahu anak bahwa ia memiliki induk baru. Tapi anak itu tidak bisa diubah.
- Misalkan Anda berhasil memecahkan masalah itu: ketika Anda memasukkan karakter baru ke dalam buffer edit, posisi absolut setiap node yang dipetakan ke posisi setelah titik itu berubah. Ini membuat sangat sulit untuk membuat struktur data yang persisten, karena setiap pengeditan dapat mengubah rentang sebagian besar node!
Tetapi di tim Roslyn kami secara rutin melakukan hal-hal yang tidak mungkin. Kami sebenarnya melakukan hal yang mustahil dengan memelihara dua pohon parse. Pohon "hijau" tidak dapat diubah, tetap, tidak memiliki referensi induk, dibuat "dari bawah ke atas", dan setiap node melacak lebarnya tetapi bukan posisi absolutnya . Ketika pengeditan terjadi, kami hanya membangun kembali bagian dari pohon hijau yang terpengaruh oleh pengeditan, yang biasanya sekitar O (log n) dari total parse node di pohon.
Pohon "merah" adalah fasad kekal yang dibangun di sekitar pohon hijau; itu dibangun "top-down" sesuai permintaan dan dibuang pada setiap edit. Ini menghitung referensi induk dengan membuatnya sesuai permintaan saat Anda turun melalui pohon dari atas . Ini menghasilkan posisi absolut dengan menghitungnya dari lebar, sekali lagi, saat Anda turun.
Anda, sebagai pengguna, hanya pernah melihat pohon merah; pohon hijau adalah detail implementasi. Jika Anda mengintip ke dalam status internal node parse, Anda sebenarnya akan melihat bahwa ada referensi ke node parse lain di sana dengan tipe yang berbeda; itulah simpul pohon hijau.
Kebetulan, ini disebut "pohon merah / hijau" karena itulah warna penanda papan tulis yang kami gunakan untuk menggambar struktur data dalam pertemuan desain. Tidak ada arti lain dari warna.
Manfaat dari strategi ini adalah kita mendapatkan semua hal hebat itu: kekekalan, ketekunan, referensi orang tua, dan sebagainya. Biayanya adalah sistem ini rumit dan dapat menghabiskan banyak memori jika fasad "merah" menjadi besar. Saat ini kami sedang melakukan eksperimen untuk melihat apakah kami dapat mengurangi sebagian biaya tanpa kehilangan manfaatnya.