Naive Implementierungen würden dieses Problem in der Tat aufdecken. Wenn Sie eine neue Datenstruktur erstellen, anstatt eine vorhandene zu aktualisieren, müssen Sie einen gewissen Overhead haben.
Verschiedene Sprachen haben unterschiedliche Arten, damit umzugehen, und es gibt einige Tricks, die die meisten von ihnen anwenden.
Eine Strategie ist die Speicherbereinigung . In dem Moment, in dem die neue Struktur erstellt wurde oder kurz danach, werden Verweise auf die alte Struktur ungültig, und der Garbage Collector wird sie je nach GC-Algorithmus sofort oder früh genug abrufen. Dies bedeutet, dass der Overhead zwar noch besteht, aber nur vorübergehend ist und nicht linear mit der Datenmenge wächst.
Eine andere ist die Auswahl verschiedener Arten von Datenstrukturen. Wo Arrays die Datenstruktur der Zielliste in imperativen Sprachen sind (normalerweise in einen dynamischen Neuzuweisungscontainer wie std::vector
in C ++ eingeschlossen), bevorzugen funktionale Sprachen häufig verknüpfte Listen. Bei einer verknüpften Liste kann eine Voranstellungsoperation ('cons') die vorhandene Liste als Endpunkt der neuen Liste wiederverwenden, sodass nur der neue Listenkopf zugewiesen wird. Ähnliche Strategien gibt es für andere Arten von Datenstrukturen - Mengen, Bäume, wie Sie es nennen.
Und dann gibt es eine faule Bewertung à la Haskell. Die Idee ist, dass von Ihnen erstellte Datenstrukturen nicht sofort vollständig erstellt werden. Stattdessen werden sie als "Thunks" gespeichert (Sie können sich diese als Rezepte für die Erstellung des Werts vorstellen, wenn er benötigt wird). Erst wenn der Wert benötigt wird, wird der Thunk zu einem tatsächlichen Wert erweitert. Dies bedeutet, dass die Speicherzuweisung aufgeschoben werden kann, bis eine Auswertung erforderlich ist, und zu diesem Zeitpunkt mehrere Thunks in einer Speicherzuweisung kombiniert werden können.