Hangi veri yapılarını kullanarak O (1) kaldırma ve değiştirme hakkını elde edebilirsiniz? Veya söz konusu yapılara ihtiyaç duyduğunuz durumlarda nasıl önleyebilirsiniz?
ST
Haskell monad mükemmel yapar.
Hangi veri yapılarını kullanarak O (1) kaldırma ve değiştirme hakkını elde edebilirsiniz? Veya söz konusu yapılara ihtiyaç duyduğunuz durumlarda nasıl önleyebilirsiniz?
ST
Haskell monad mükemmel yapar.
Yanıtlar:
İtfa edilmiş sabit zaman veya hatta ( sıralar gibi bazı sınırlı durumlar için) birçok sorun için sabit zaman güncellemesi elde etmek için tembellikten ve diğer numaralardan yararlanan çok çeşitli veri yapıları vardır . Chris Okasaki'nin doktora tezi "Tamamen İşlevsel Veri Yapıları" ve aynı adı taşıyan kitap başlıca bir örnektir (belki de ilk büyük olanıdır), ancak alan o zamandan bu yana ilerlemiştir . Bu veri yapıları tipik olarak sadece arayüzde sadece işlevsel değildir, aynı zamanda saf Haskell ve benzer dillerde de uygulanabilir ve tamamen kalıcıdır.
Bu gelişmiş araçların hiçbiri olmasa bile, basit dengeli ikili arama ağaçları logaritmik zaman güncellemeleri verir, böylece değiştirilebilir bellek en azından logaritmik bir yavaşlama ile simüle edilebilir.
Hile olarak kabul edilebilecek, ancak uygulama çabası ve gerçek dünya performansı açısından çok etkili olan başka seçenekler de var. Örneğin, doğrusal türler veya benzersiz türler, programın önceki değere (mutasyona uğrayacak bellek) geçmesini önleyerek kavramsal olarak saf bir dil için uygulama stratejisi olarak yerinde güncelleştirmeye izin verir. Bu, kalıcı veri yapılarından daha az geneldir: Örneğin, durumun önceki tüm sürümlerini depolayarak geri alma günlüğünü kolayca oluşturamazsınız. Yine de güçlü bir araçtır, ancak AFAIK ana fonksiyonel dillerde henüz mevcut değildir.
Değişebilir durumu fonksiyonel bir ayara güvenli bir şekilde sokmak için başka bir seçenek ST
Haskell'deki monad'tır. Mutasyon olmadan ve engelleme unsafe*
işlevleri olmadan uygulanabilir, kalıcı bir veri yapısını dolaylı olarak iletme etrafındaki sadece süslü bir sarmalayıcıymış gibi davranır (bkz. State
). Ancak değerlendirme sırasını zorlayan ve kaçmayı önleyen bazı tip sistem kanunları nedeniyle, tüm performans avantajlarıyla birlikte yerinde mutasyonla güvenle uygulanabilir.
Bir ucuz değiştirilebilir yapı argüman yığınıdır.
Tipik SICP tarzı faktoring hesaplamasına bir göz atın:
(defn fac (n accum)
(if (= n 1)
accum
(fac (- n 1) (* accum n)))
(defn factorial (n) (fac n 1))
Gördüğünüz gibi, ikinci argüman fac
hızlı değişen ürünü içeren değişken bir akümülatör olarak kullanılıyorn * (n-1) * (n-2) * ...
. Değişken değişken görünmemektedir, ancak akümülatörü örneğin başka bir ipliğin istemeden değiştirmesinin bir yolu yoktur.
Bu, elbette sınırlı bir örnek.
Baş düğümünün ucuz bir şekilde değiştirilmesiyle (ve baştan başlayarak herhangi bir parçasını uzatarak) değişmez bağlantılı listeler elde edebilirsiniz: sadece yeni baş noktasını eski baş ile aynı düğüme yönlendirirsiniz. Bu, birçok liste işleme algoritmasıyla iyi çalışır (herhangi bir şeyfold
temelli) iyi çalışır.
Örneğin HAMT'lere dayanan ilişkisel dizilerden oldukça iyi performans alabilirsiniz . Mantıksal olarak, bazı anahtar / değer çiftleri değiştirilmiş olarak yeni bir ilişkisel dizi alırsınız. Uygulama, eski ve yeni oluşturulan nesneler arasındaki ortak verilerin çoğunu paylaşabilir. Bu olsa O (1) değildir; genellikle en azından en kötü durumda, logaritmik bir şey elde edersiniz. Diğer taraftan, değişmeyen ağaçlar, genellikle değişken ağaçlara kıyasla performans cezası çekmezler. Tabii ki, bu genellikle engelleyici olmaktan uzak, bazı ek yükler gerektirir.
Başka bir yaklaşım, bir ağaç bir ormana düşerse ve hiç kimse onu duymazsa, ses üretmemesi gerektiği fikrine dayanır. Yani, bir miktar değişimli durumun hiçbir zaman yerel bir kapsam dışına çıkmadığını ispatlayabilirseniz, içindeki verileri güvenle aktarabilirsiniz.
Clojure, yerel kapsam dışında sızmayan değişmez veri yapılarının değişmez 'gölgeleri' olan geçici olaylara sahiptir . Temiz , benzer bir şey elde etmek için Uniques kullanır (doğru hatırlıyorsam). Rust, statik olarak kontrol edilmiş benzersiz işaretçilerle benzer şeyler yapmanıza yardımcı olur.
ref
ve bunları belirli bir kapsamda sınırlandırma yöntemi vardır . IORef
Veya bakınız STRef
. Ve sonra tabii ki benzer olan ama aklı başında eşzamanlı semantiği olan TVar
s ve MVar
s var ( TVar
s için s ve muteks için stm MVar
)
İstediğiniz şey biraz fazla geniş. O (1) çıkarma ve hangi pozisyondan değiştirme? Bir dizinin başı? Kuyruk? Keyfi bir pozisyon mu? Kullanılacak veri yapısı bu detaylara bağlıdır. Bununla birlikte, 2-3 Parmak Ağaç , orada çok yönlü kalıcı veri yapılarından birine benziyor:
2-3 parmak ağacı, amortize edilmiş sabit zamanda uçlara erişimi destekleyen kalıcı sekansların işlevsel bir temsili ve küçük parçanın boyutunda logaritmik zamanla birleştirme ve ayrılma.
(...)
Ayrıca, bölünmüş işlemi genel bir biçimde tanımlayarak, bir dizi, öncelik sırası, arama ağacı, öncelikli arama sırası ve daha fazlasını yapabilen genel amaçlı bir veri yapısı elde ederiz.
Genel olarak kalıcı veri yapıları, keyfi konumları değiştirirken logaritmik performansa sahiptir. Bu bir problem olabilir veya olmayabilir, çünkü O (1) algoritmasındaki sabit yüksek olabilir ve logaritmik yavaşlama daha yavaş bir genel algoritmaya "emilebilir".
Daha da önemlisi, kalıcı veri yapıları, programınız hakkında mantıklı olmayı kolaylaştırır ve bu her zaman varsayılan çalışma modunuz olmalıdır. Kalıcı veri yapılarını mümkün olduğunda tercih etmeli ve kalıcı veri yapısının performans darboğazı olduğunu belirledikten ve belirledikten sonra yalnızca değişken bir veri yapısı kullanmalısınız. Her şey erken optimizasyondur.