Eksi listeleri neden işlevsel programlama ile ilişkili?


22

İşlevsel dillerin çoğunun, en temel liste türleri olarak tek bir bağlantılı liste ("eksiler" listesi) kullandığını fark ettim. Örnekler arasında Common Lisp, Haskell ve F #. Bu, yerel liste türlerinin diziler olduğu genel dillerden farklıdır.

Neden?

Common Lisp (dinamik olarak yazılmış) için, eksilerin listelerin, ağaçların vb. Temeli olacak kadar genel olduğu fikrine kapılıyorum. Bu küçük bir neden olabilir.

Statik olarak yazılmış diller için iyi bir mantık bulamıyorum, hatta karşı-argümanları bile bulabiliyorum:

  • İşlevsel stil değişmezliği teşvik eder, bu nedenle bağlantılı listenin yerleştirme kolaylığı bir avantajdan daha düşüktür,
  • İşlevsel stil değişmezliği teşvik eder, aynı zamanda veri paylaşımını; Bir dizinin "kısmen" paylaşılması, bağlantılı bir listeden daha kolaydır,
  • Düzenli bir dizide desen eşleştirmesini de yapabilir ve hatta daha iyisini yapabilirsiniz (örneğin sağdan sola kolayca katlayabilirsiniz),
  • Bunun üzerine ücretsiz rastgele erişim
  • Ve (pratik bir avantaj) Dil statik olarak yazılmışsa, düzenli bir bellek düzeni kullanabilir ve önbellekten hızlanmayı sağlayabilirsiniz.

Peki neden bağlantılı listeleri tercih ediyorsun?


4
@ Sepp2k'nin cevabı üzerine yapılan yorumlardan yola çıkarak, an array is easier to share "partially" than a linked listne demek istediğinizi netleştirmeniz gerektiğini düşünüyorum . Özyinelemeli doğası nedeniyle, anladığım gibi doğrudur - bir dizinin yeni bir kopya oluşturmak için zaman harcaması gerekirken, içindeki herhangi bir düğümü geçirerek bağlantılı bir listeyi kısmen paylaşabilirsiniz. Veya veri paylaşımı açısından, iki bağlantılı liste aynı düzene işaret edebilir, bu da sadece düz dizilerde mümkün değildir.
Izkata,

Bir dizi kendini ofset, uzunluk, arabellek üçlü olarak tanımlarsa, o zaman ofset + 1, length-1, buffer ile yeni bir tane oluşturarak bir diziyi paylaşabilirsiniz. Veya alt dizi olarak özel bir dizi türünüz olsun.
Dobes Vandermeer 17:12

@Izkata Dizilerden bahsederken, nadiren sadece C'deki bitişik belleğin başlangıcını gösteren bir işaretçi gibi bir tamponu kastediyoruz. Genellikle, uzunluğu ve işaretleyiciyi sarılmış bir tamponun başlangıcına yerleştiren bir tür yapı kastediyoruz. Bu tür bir sistemde, bir dilimleme işlemi, tampon imleci orta tamponu (alt dizinin ilk öğesinde) ortada işaret eden ve sayımı start + sayımı size son öğeyi verecek şekilde olan bir alt diziyi döndürebilir. Bu tür dilimleme işlemleri zaman ve mekanda O (1) 'dir.
Alexander - Reinstate Monica

Yanıtlar:


22

En önemli faktör, O (1) zamanında değişmez bir şekilde tek tek bir listeye hazırlanabilmenizdir; bu, n-element listelerini yinelemeli olarak O (n) zamanında oluşturmanıza olanak sağlar:

// Build a list containing the numbers 1 to n:
foo(0) = []
foo(n) = cons(n, foo(n-1))

Bunu değişmez diziler kullanarak yaptıysanız, çalışma zamanı ikinci dereceden olacaktır, çünkü her consişlemin tüm diziyi kopyalaması gerekir, bu da ikinci dereceden bir çalışma süresine yol açar.

İşlevsel stil değişmezliği teşvik eder, aynı zamanda veri paylaşımını; bir dizinin "kısmen" paylaşılması bağlantılı bir listeden daha kolaydır.

“Kısmen” paylaşarak, O (1) zamanındaki bir diziden alt dizilimi alabileceğiniz anlamına gelir, oysa bağlı listelerde kuyruğu sadece O (1) zamanla alabilir ve diğer her şey O (n) 'ye ihtiyaç duyar. Bu doğru.

Ancak birçok durumda kuyruk almak yeterlidir. Ayrıca, ucuz bir şekilde alt diziler oluşturabilmenin, eğer ucuz bir şekilde dizileri oluşturmanın bir yolu yoksa, size yardımcı olamayacağını unutmamalısınız. Ve (akıllı derleyici optimizasyonları olmadan) bir diziyi adım adım ucuz bir şekilde oluşturmanın bir yolu yoktur.


Bu hiç doğru değil. Amortize O (1) 'deki dizileri ekleyebilirsiniz.
DeadMG

10
Evet ama hiç @DeadMG değişmez diziler.
sepp2k

"kısmen paylaşma" - iki eksenin de listesinin aynı sonek listesine işaret edebileceğini düşünüyorum (neden bunu isteyeceğinizi bilemeyin) ve listenin başlangıcı yerine orta noktayı kopyalamak zorunda kalmadan başka bir işleve geçirebilirsiniz o (Bunu defalarca yaptım)
Izkata

@ İzkata OP, listeleri değil, kısmen dizileri paylaşmaktan bahsediyordu. Ayrıca kısmi paylaşım olarak adlandırdığınızı asla duymadım . Bu sadece paylaşıyor.
sepp2k

1
@ İzkata OP, "dizi" terimini üç kez kullanır. Bir keresinde, FP dilleri, diğer dillerin dizileri kullandığı bağlantılı listeleri kullanır. Bir kere dizilerin kısmi paylaşımda, bağlantılı listelerden daha iyi olduğunu ve bir kere dizilerin de aynı şekilde (eşleştirilmiş listeler olarak) eşleştirilebileceğini söylemek için. Her durumda dizileri ve bağlantılı listeleri zıt kılar (dizilerin birincil veri yapısı olarak bağlantılı listelerden daha kullanışlı olacağını göstermesi için, neden bağlantılı listelerin FP'de tercih edildiğini sorusuna yöneltir), terimleri birbirinin yerine kullanıyor olabilir.
sepp2k

4

İşlev kodunda kolayca uygulanabilecek listeler olduğunu düşünüyorum.

Şema:

(define (cons x y)(lambda (m) (m x y)))

Haskell:

data  [a]  =  [] | a : [a]

Diziler daha zordur ve uygulanması neredeyse hiç hoş değildir. Son derece hızlı olmalarını istiyorsanız o zaman düşük seviyeli yazılmalıdır.

Ek olarak, özyineleme dizilerden çok listelerde daha iyi çalışır. Diziyi dizine ekleyerek, tekrarlı olarak tükettiğiniz / oluşturduğunuz bir liste sayısını düşünün.


Şema sürümünüzü bağlantılı listelerin bir uygulaması olarak adlandırmanın doğru olduğunu söyleyemem. İşlevlerden başka hiçbir şeyi saklamak için kullanamazsınız. Ayrıca diziler, kendileri için yerleşik bir desteği olmayan herhangi bir dilde (veya bellek parçaları) uygulamakta zorlanır (aslında imkansızdır), bağlantılı listelerde yalnızca yapıların, sınıfların, kayıtların veya cebirsel veri türlerinin uygulanması gibi bir şey gerekir. Bu işlevsel programlama dillerine özgü değildir.
sepp2k

@ sepp2k "Fonksiyondan başka bir şey sakla" derken ne demek istiyorsun ?
Pubby

1
Demek istediğim, bu şekilde tanımlanmış listelerin fonksiyon olmayan hiçbir şeyi depolayamayacağıydı. Bu aslında doğru değil. Dunno, neden böyle düşündüm. Bunun için üzgünüm.
sepp2k

2

Tek bağlantılı bir liste, en basit kalıcı veri yapısıdır .

Kalıcı veri yapıları, performans ve tamamen işlevsel programlama için çok önemlidir.


1
Bu, sadece 4 yıl önce yayınlanan en iyi cevabın verdiği ve açıklandığı noktayı tekrarlıyor gibi görünüyor
gnat

3
@gnat: En üstteki cevap kalıcı veri yapılarından bahsetmiyor ya da tek başına bağlı listelerin en basit kalıcı veri yapısı olduğu veya tamamen işlevsel programlama için gerekli olduklarından bahsetmiyor. En iyi cevapla hiçbir örtüşme bulamıyorum.
Michael Shaw,

2

Cons düğümlerini, yalnızca bir çöp toplanmış dili varsa kolayca kullanabilirsiniz.

Eksileri düğümleri özyinelemeli aramaların işlevsel programlama tarzı ve değişmez değerleri ile çok eşleşir. Bu yüzden zihinsel programcı modeline iyi uyuyor.

Ve tarihi sebepleri de unutma. Neden hala Cons Nodları olarak adlandırılıyorlar ve daha da kötüsü hala otomobil ve cdr'yi erişimciler olarak kullanıyorlar? İnsanlar onu ders kitaplarından ve derslerden öğrenip kullanırlar.

Haklısın, gerçek dünyada dizilerin kullanımı çok daha kolay, hafıza alanının sadece yarısını tüketiyor ve önbellek seviyesi özlüyor olması nedeniyle çok daha fazla performans gösteriyor. Bunları zorunlu dillerle kullanmak için hiçbir sebep yoktur.


1

Bağlantılı listeler aşağıdaki nedenlerden dolayı önemlidir:

3 gibi bir sayı alıp, gibi bir halef sırasına dönüştürdüğünüzde, onunla succ(succ(succ(zero)))ikame kullandığınızda {succ=List node with some memory space}ve {zero = end of list}sonunda bir bağlantı listesi (uzunluk 3) olacaktır.

Asıl önemli kısım sayılar, yer değiştirme ve hafıza alanı ve sıfırdır.

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.