Tamamen işlevsel programlama dilleri hızlı değişen verilerle nasıl başa çıkıyor?


22

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?


2
Tamamen işlevsel programlama dillerine daha az aşina olan bizler için, biraz daha arka plan sağlayabileceğinizi düşünüyor musunuz, böylece probleminizin ne olduğunu anlıyoruz?
SinirliFormsDesigner'la

4
@FrustratedWithFormsDesigner Tamamen işlevsel programlama dilleri, tüm değişkenlerin değişmez olmasını gerektirir; bu da "değiştirildiğinde" kendilerinin yeni sürümlerini oluşturan veri yapılarını gerektirir.
Doval

5
Okasaki'nin tamamen işlevsel veri yapıları konusundaki çalışmalarının farkında mısınız?

2
Bir olasılık değişken veriler için bir monad tanımlamaktır (bkz. Örneğin haskell.org/ghc/docs/4.08/set/sec-marray.html ). Bu şekilde, değişken veriler IO'ya benzer şekilde işlem görür.
Giorgio

1
Bununla birlikte, bu değişmez yapılar tipik olarak basit dizilerden çok daha fazla ek yüke sahiptir. Sonuç olarak, orada olduğunu çok pratik bir fark vardır. Bu nedenle, genel amaçlı programlamayı amaçlayan tümüyle işlevsel bir dilin, değişebilir yapıları saf anlambilimiyle uyumlu bir şekilde güvenli bir şekilde kullanmanın bir yolu olmalıdır. STHaskell monad mükemmel yapar.
leftaroundabout

Yanıtlar:


32

İ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 STHaskell'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.


Ayrıca, bir listede veya ağaçta bir odakta hızlı değişiklikler yapma yeteneği veren Fermuarlardan bahsetmeye değer olabilir
jk.

1
@jk. Bağlantılı olduğum Teorik Bilgisayar Bilimi yazısında bahsedilir . Dahası, bunlar birçok ilgili veri yapısından sadece bir tanesidir (hepsi bir sınıf) ve hepsini tartışmak kapsam dışıdır ve çok az kullanışlıdır.

yeterince adil, bağlantıları takip etmemişti
jk.

9

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 fachı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.


1
+ 1, ayrıca Temiz'te benzersiz türlerden bahsetmek için de kullanılır.
Giorgio

@ 9000 Haskell'in Clojure'un geçici durumlarına benzer bir şey olduğunu duydum - yanılıyorsam biri beni düzeltiyor.
Paul,

@paul: Haskell hakkında çok elverişli bir bilgiye sahibim, bu yüzden bilgilerimi (en azından google anahtar kelimesi) sağlayabilseydiniz, cevaba mutlu bir şekilde başvururdum.
9000

1
@paul Çok emin değilim. Ancak Haskell'in ML'lere benzer bir şey yaratma refve bunları belirli bir kapsamda sınırlandırma yöntemi vardır . IORefVeya bakınız STRef. Ve sonra tabii ki benzer olan ama aklı başında eşzamanlı semantiği olan TVars ve MVars var ( TVars için s ve muteks için stm MVar)
Daniel Gratzer

2

İ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.

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.