Scala'da bir listeye başvurmak neden O (n) zaman karmaşıklığına sahip?


13

Sadece bir List(: +) için ekleme işleminin yürütme süresinin boyutu ile doğrusal olarak büyüdüğünü okudum List.

Bir şeye eklemek Listoldukça yaygın bir işlem gibi görünüyor. Bunu yapmanın deyimsel yolu neden bileşenleri önceden ve sonra listeyi tersine çevirmeli? Uygulama herhangi bir noktada değiştirilebileceği için tasarım hatası da olamaz.

Benim açımdan, hem ön hem de ekleme O (1) olmalıdır.

Bunun meşru bir nedeni var mı?


2
"Meşru" tanımınıza bağlıdır. Scala, büyük ölçüde değişmez veri yapıları, her yerde bulunan anonim listeler, fonksiyonel kompozisyon, vb. İçine girer. Varsayılan liste uygulaması (liste kuyruğuna ekstra değiştirilebilir bir işaretçi olmadan) bu stil için iyi çalışır. Daha güçlü bir listeye ihtiyacınız varsa, en azından standart olanlardan neredeyse ayırt edilemeyen kendi kabınızı yazmak çok kolaydır.
Kilian Foth

1
Siteler arası ilgili - Scala'daki bir listenin sonuna bir öğe eklemek - orada bunun doğası hakkında biraz var. Görünen Scala bir liste O (K) olan bu sebepten bunu kopyalamanız gerekir, değişmez olduğunu.

Scala'nın sağladığı birçok kullanılabilir veri yapısından birini veya O (1) ekleme süresi (Vector) ile değişmez veri yapılarını kullanabilirsiniz. List[T]bunu saf işlevsel bir dilde kullandığınız şekilde varsayar - genellikle yapısöküm ve prepends ile baştan başlar.
KChaloux

3
Ön ek, yeni kafanın sonraki düğüm işaretçisini var olan değişmez listeye koyacaktır - bu değiştirilemez. İşte O (1).

1
Veri yapısı genel konusundaki seminal çalışma ve analiz için saf FP'de karmaşıklık ölçümleri , daha sonra bir kitap olarak yayınlanan Okasaki tezini okudu . FP'de veri düzenlemeyi düşünmeyi anlamak için bazı FP öğrenen herkes için son derece saygın ve çok iyi bir okuma. Ayrıca iyi yazılmış ve okunması ve takip edilmesi kolay bir şekilde kaliteli bir metin.
Jimmy Hoffa

Yanıtlar:


24

Yorumumu biraz genişleteceğim. 'Den gelen List[T]veri yapısı, scala.collection.immutabledaha tamamen işlevsel bir programlama dilinde değişmez bir listenin çalıştığı şekilde çalışacak şekilde optimize edilmiştir. Çok hızlı ön sürelere sahip ve neredeyse tüm erişiminiz için kafa üzerinde çalışacağınız varsayılıyor.

Değişmez listeler, bağlantılı listelerini bir dizi "eksilerini" olarak modelledikleri için çok hızlı ön sürelere sahip olurlar. Hücre, tek bir değer ve sonraki hücreye (klasik tek bağlantılı liste stili) bir işaretçi tanımlar:

Cell [Value| -> Nil]

Bir listenin başına geçtiğinizde, mevcut listenin geri kalanının işaret edildiği şekilde, yalnızca tek bir yeni hücre oluşturursunuz:

Cell [NewValue| -> [Cell[Value| -> Nil]]

Liste değiştirilemediğinden, bunu gerçek bir kopyalama olmadan yapmak güvenlidir . Eski listenin değiştirilmesi ve yeni listenizdeki tüm değerlerin geçersiz olmasına neden olma tehlikesi yoktur. Ancak, uzlaşma olarak listenizin sonuna değişebilir bir işaretçi ekleme yeteneğini kaybedersiniz .

Bu, listeler üzerinde özyineli olarak çalışmaya çok iyi borç verir. Diyelim ki kendi sürümünüzü tanımladınız filter:

def deleteIf[T](list : List[T])(f : T => Boolean): List[T] = list match {
  case Nil => Nil
  case (x::xs) => f(x) match {
    case true => deleteIf(xs)(f)
    case false => x :: deleteIf(xs)(f)
  }
}

Bu, listenin başından özel olarak çalışan ve :: çıkarıcı ile desen eşleşmesinden yararlanan özyinelemeli bir işlevdir. Bu, Haskell gibi dillerde çokça gördüğünüz bir şey.

Gerçekten hızlı ekler istiyorsanız, Scala aralarından seçim yapabileceğiniz çok sayıda değiştirilebilir ve değişmez veri yapısı sunar. Değişken tarafta, içine bakabilirsiniz ListBuffer. Alternatif olarak, Vectorgelen scala.collection.immutablehızlı ekleme zamanı vardır.


şimdi anladım! Tam mantıklı.
DPM

Scala bilmiyorum ama bu elsesonsuz bir döngü değil mi? Bence böyle bir şey olmalı x::deleteIf(xs)(f).
svick

@svick Uh ... evet. Evet öyle. Hızlı bir şekilde yazdım ve kodumu doğrulamadım, çünkü gitmek için bir toplantı yaptım: p (Şimdi düzeltilmelidir!)
KChaloux

Çünkü @Jubbat headve tailhızlı herhangi karma tabanlı harita ya da dizi kullanmaktan daha - - Listedeki bu tür erişim çok hızlı o özyinelemeli fonksiyonlar için mükemmel bir türüdür. Listelerin çoğu işlevsel dilde (örn. Haskell veya Şema) çekirdek bir tür olmasının bir nedeni budur
itsbruce

Mükemmel cevap. Belki de bir TL ekleyeceğim; sadece "çünkü eklemeniz gerekir, eklememeniz gerekir" diyen DR (çoğu geliştiricinin Lists ve ekleme / ekleme ile ilgili temel varsayımların temizlenmesine yardımcı olabilir ).
Daniel B
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.