Nesne-grafik mutasyonunu değişmez durumlarla verimli bir şekilde temsil etmek mümkün müdür?


12

C ++ değişmez nesne kullanarak pratik yapıyorum. Benim kişisel hedefim, değişmeyen grafik dizisiyle genel nesne grafiğini (yığın halinde) temsil etmektir.

Çok versiyonlu grafiğin kendisini oluşturmak o kadar zor değil. Sorun performans. Kaba kuvvet versiyonlaması grafiğin tam kopyasına ihtiyaç duyuyor ve bu kabul edilemezdi.

Değişmeyen düğümleri paylaşmaya çalıştım. Ama bu durumda yeni bir sorunum var; Referanslar. Başka bir nesneye yapılan referans tüm grafikte güncellenmelidir. Bu, her yeni grafik sürümü elde ettiğimde tüm düğümleri ziyaret etmelidir. Bu da düğümleri referanslarla değiştirir, bu yüzden de türetilmelidir (kopyalayarak). Performans, kaba kuvvetli kopyalamadan daha iyi olmayacaktır.

Tahmin edebildiğim kadarıyla, nesne grafiğinin değişmez durumlarla mutasyonunu temsil etmenin gerçek ve etkili bir yolu yoktur. Bu yüzden bu konuda biraz fikir istiyorum.

Nesne grafiğinin mutasyonunu değişmez bir durumla verimli bir şekilde temsil etmek mümkün müdür?


1
Bu sadece zor çünkü kenarları düğümlere koyuyorsunuz. Kenarları harici olarak, değişmez bir koleksiyonda saklarsanız, kolay olurdu.
dan_waterworth

@ dan_waterworth Bitişiklik listesi kullanırsam, her seferinde her kenarda arama yapmam gerekir. Bu yüzden okuma ve yazma performansı arasında bir denge olduğunu düşünüyorum.
Eonil

1
Bir liste olmak zorunda değil.
dan_waterworth

@dan_waterworth B-ağacı gibi bir şey mi demek istiyorsun?
Eonil

2
B-ağaçları gibi geniş ağaçlar, depolama ortamına olan gecikme süresi yüksek olduğunda kullanılma eğilimindedir. Bu durumda, daha dar bir şeyle daha iyi olabilirsiniz, ancak evet, bir tür dengeli arama ağacı iyi bir fikir olacaktır.
dan_waterworth

Yanıtlar:


11

Aradığınıza Kalıcı Veri Yapısı denir . Kalıcı veri yapıları için kanonik kaynak Chris Okasaki'nin Tamamen Fonksiyonel Veri Yapıları Kitabı'dır . Kalıcı veri yapıları, Clojure ve Scala'da popüler olmaları nedeniyle son zamanlarda ilgi topladı.

Ancak, tuhaf bir nedenden dolayı, Kalıcı Grafikler çoğunlukla göz ardı ediliyor gibi görünüyor. Listelerimiz, düzinelerce farklı ağaç türü, diziler, öncelik kuyrukları, haritalar var, ancak grafikler yok.

Gerçekten sadece bir makale buldum: Tamamen Kalıcı Grafikler - Hangisini Seçmeli?


4

Nesneler arasındaki bağlantıları, sürümlendirilmiş kaynağınızın bir parçası olarak görmüyorsanız (ve muhtemelen - bu durumda aşağıdakiler muhtemelen pek yardımcı olmuyorsa), nesnelerinizi bağlantı bölümünü temsil eden bir parçaya bölmeyi düşünebilirsiniz. nesnenin ve değişmez durumu temsil eden bir parçanın.

Daha sonra, bağlantı alt nesnelerinin her birinin sürüm durumlarının bir vektörünü içermesini sağlayabilirsiniz. Bu şekilde uygun değişmez duruma erişmek için grafik sürüm numarası ile çalışabilirsiniz.

Belirli bir düğümde güncelleme olduğunda grafiğin tamamını geçmekten kaçınmak için, bir düğüme düğümün geçerli sürüm numarasından daha büyük bir sürüm numarasıyla erişilirse, geçerli sürüm kullanılır. . Düğüm daha sonra güncellenirse, tüm ara sürümleri önceki sürümle doldurursunuz - böylece nesne grafiğinde tembel güncellemeler yapmanıza izin verir.

Nesneler arasındaki bağlantı sürüm durumunuzun bir parçasıysa, yukarıdakiler çalışmaz. Ama belki de aşağıdaki gibi uzatabilirsiniz:

Grafikteki her nesne için bir "tanıtıcı nesne" oluşturun. Tanıtıcı nesne, sürümde değiştirilemeyen durumların listesini içerir. Nesne referanslarını grafiğin herhangi bir nesnesine depolamak yerine, tanıtıcı nesneye bir başvuru depolarsınız. Daha sonra nesnelere yapılan her başvuru, tanıtıcı ve o anda işlenmekte olan nesne grafiğinin sürüm numarası kullanılarak tanıtıcı aracılığıyla yeniden gönderilir. Bu, nesne için doğru değişmez durumu döndürecektir. Değişmez durumlar, grafikteki diğer nesnelere başvurmak için tutamaçları kullanır, böylece her zaman grafiğin işlemek istediğiniz sürümü için tutarlı tarih elde edersiniz.

Yukarıda açıklanan aynı mantık, tutamaçlardaki sürümlerin güncellenmesi için geçerlidir - bu da tembel, yerelleştirilmiş güncellemelere izin verir.


3

Bu soruna çok iyi itfa edilmiş zaman karmaşıklığı ile yayınlanmış bir çözüm var, ancak tam olarak ne arayacağınızı bilmediğinizde bulmak zor. Bu ders notlarında güzel bir özet bulabilirsiniz (bölüm 2.2.3'ü kontrol edin) veya Veri Yapılarını Kalıcı hale getirmek için orijinal makaleyi okuyabilirsiniz . Nesne grafiğinizin sınırlı bağlantısı varsa (örn. Ağaç benzeri yapılar) etkileyici olan değişmez grafiği okumak ve güncellemek için amortismanlı O (1) karmaşıklığına bile erişebilirsiniz.

Fikir, her nesnenin, mevcut değişmez durumunu depolamanın yanı sıra, değişiklikleri kaydetmek için yer ayırmasıdır. Değişiklikleri uygulayarak mevcut bir grafikten yeni bir sabit grafik oluşturmak istediğinizde, iki durumu göz önünde bulundurmanız gerekir:

  • Değiştirmek istediğiniz nesnenin hala değişiklik alanı var. Değişiklikleri doğrudan nesnede saklayabilirsiniz.

  • Nesnede artık değişiklik için yer yok. Geçerli değerleri ve boş değişiklik kayıtlarını temel alan yeni bir örnek oluşturursunuz. Şimdi, yeni nesneye başvuruda bulunmak için eski nesneye başvuran tüm nesneleri özyinelemeli olarak güncelleştirmeniz gerekir.

    Referans nesnesinin kendisinde değişiklikler için hala yer varsa, değişikliği doğrudan referansta saklayabilirsiniz, aksi takdirde özyinelemeli olarak basamaklandırılır.

Bu şema, değişmez nesne grafiklerinin yeni nesillerinin verimli bir şekilde oluşturulmasını desteklese de, ondan okumayı zorlaştırır, çünkü şimdi değişmez bir nesneden veri okurken hangi "sürüm" e erişmek istediğinizi belirtmeniz gerekir. Bunun nedeni, değiştirilemeyen nesnenin saklanan değişiklik kayıtları nedeniyle birden çok sürüm için bilgiye sahip olabilmesidir, bu nedenle hangi sürüme bakmak istediğinizi belirtmeniz gerekir.

Bunu uygularken API içindeki bu karmaşıklığı gizlemeye çalışıyorum. Değişmez grafikte dışarıdan bir şeye atıfta bulunurken, doğrudan referanslar yerine oluşturulan saplamalar kullanıyorum, bu nedenle arayanların istenen sürümü manuel olarak geçirmeye devam etmeleri gerekmiyor. Bu, referansları doğrudan bir işaretçiden biraz daha pahalı hale getirir, ancak kolaylık sağlamaya değer buluyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.