Mir ist aufgefallen, dass die meisten funktionalen Sprachen eine einfach verknüpfte Liste (eine "Nachteile" -Liste) als ihre grundlegendsten Listentypen verwenden. Beispiele hierfür sind Common Lisp, Haskell und F #. Dies unterscheidet sich von Standardsprachen, bei denen die systemeigenen Listentypen Arrays sind.
Warum das?
Für Common Lisp (dynamisch getippt) habe ich die Idee, dass die Nachteile allgemein genug sind, um auch die Basis für Listen, Bäume usw. zu sein. Dies könnte ein winziger Grund sein.
Für statisch typisierte Sprachen kann ich jedoch keine gute Begründung finden, ich kann sogar Gegenargumente finden:
- Der funktionale Stil fördert die Unveränderlichkeit, daher ist das einfache Einfügen der verknüpften Liste weniger von Vorteil.
- Der funktionale Stil fördert die Unveränderlichkeit, also auch den Datenaustausch. Ein Array lässt sich leichter "teilweise" teilen als eine verknüpfte Liste.
- Sie können den Mustervergleich auch für ein reguläres Array durchführen, und noch besser (Sie können beispielsweise leicht von rechts nach links falten).
- Darüber hinaus erhalten Sie freien Zugang,
- Und (ein praktischer Vorteil) wenn die Sprache statisch geschrieben ist, können Sie ein reguläres Speicherlayout verwenden und eine Geschwindigkeitssteigerung aus dem Cache erhalten.
Warum also verknüpfte Listen bevorzugen?
an array is easier to share "partially" than a linked list
geklärt werden, was Sie meinen. Aufgrund ihrer rekursiven Natur ist meines Wissens das Gegenteil der Fall - Sie können eine verknüpfte Liste zum Teil einfacher freigeben, indem Sie einen beliebigen Knoten darin übergeben, während ein Array Zeit für die Erstellung einer neuen Kopie benötigen würde. Bei der gemeinsamen Nutzung von Daten können zwei verknüpfte Listen auf dasselbe Suffix verweisen, was bei Arrays einfach nicht möglich ist.Antworten:
Der wichtigste Faktor ist, dass Sie einer unveränderlichen einfach verknüpften Liste in O (1) -Zeit voranstellen können, wodurch Sie rekursiv n-Element-Listen in O (n) -Zeit wie folgt aufbauen können:
Wenn Sie dies mit unveränderlichen Arrays durchführen, ist die Laufzeit quadratisch, da bei jeder
cons
Operation das gesamte Array kopiert werden muss, was zu einer quadratischen Laufzeit führt.Ich gehe davon aus, dass mit "teilweiser" Freigabe gemeint ist, dass Sie ein Subarray aus einem Array in O (1) -Zeit nehmen können, während Sie bei verknüpften Listen nur in O (1) -Zeit den Schwanz nehmen können und alles andere O (n) benötigt. Das ist wahr.
In vielen Fällen reicht es jedoch aus, den Schwanz zu nehmen. Und Sie müssen berücksichtigen, dass es Ihnen nicht hilft, Subarrays kostengünstig zu erstellen, wenn Sie keine Möglichkeit haben, Arrays kostengünstig zu erstellen. Und (ohne clevere Compiler-Optimierungen) gibt es keine Möglichkeit, ein Array Schritt für Schritt kostengünstig aufzubauen.
quelle
Ich denke, es kommt darauf an, dass Listen ziemlich einfach in funktionalen Code implementiert werden können.
Planen:
Haskell:
Arrays sind schwieriger und bei weitem nicht so schön zu implementieren. Wenn Sie möchten, dass sie extrem schnell sind, müssen sie auf niedriger Ebene geschrieben werden.
Darüber hinaus funktioniert die Rekursion in Listen viel besser als in Arrays. Betrachten Sie die Häufigkeit, mit der Sie eine Liste rekursiv konsumiert / generiert haben, im Vergleich zu einem indizierten Array.
quelle
Eine einfach verknüpfte Liste ist die einfachste persistente Datenstruktur .
Persistente Datenstrukturen sind für eine performante, rein funktionale Programmierung unerlässlich.
quelle
Sie können Cons-Knoten nur dann problemlos verwenden, wenn Sie über eine Garbage-Collected-Sprache verfügen.
Die Nachteile von Nodes stimmen weitgehend mit dem funktionalen Programmierstil von rekursiven Aufrufen und unveränderlichen Werten überein. So passt es gut in das mentale Programmiermodell.
Und vergessen Sie nicht die historischen Gründe. Warum werden sie immer noch Cons Nodes genannt und nutzen immer noch Auto und CDR als Zugriffsmethoden? Die Leute lernen es aus Lehrbüchern und Kursen und benutzen es dann.
Sie haben Recht, in der Realität sind Arrays viel einfacher zu verwenden, belegen nur die Hälfte des Speicherplatzes und sind aufgrund von Ausfällen auf Cache-Ebene viel performanter. Es gibt keinen Grund, sie mit imperativen Sprachen zu verwenden.
quelle
Verknüpfte Listen sind aus folgendem Grund wichtig:
Wenn Sie eine Zahl wie 3 nehmen und in eine Nachfolgereihenfolge wie konvertieren, verwenden Sie
succ(succ(succ(zero)))
die Substitution mit{succ=List node with some memory space}
, und{zero = end of list}
, werden Sie mit einer verknüpften Liste am Ende (der Länge 3).Der eigentliche wichtige Teil ist Zahlen, Substitution und Speicherplatz und Null.
quelle