Ich übe die Verwendung von unveränderlichen Objekten in C ++. Mein persönliches Ziel ist die Darstellung eines allgemeinen Objektgraphen (in Heap) mit einer Abfolge unveränderlicher Graphen.
Das Erstellen des Diagramms mit mehreren Versionen selbst ist nicht allzu schwierig. Das Problem ist die Leistung. Für die Brute-Force-Versionierung ist eine vollständige Kopie des Diagramms erforderlich. Dies war nicht akzeptabel.
Ich habe versucht, unveränderte Knoten zu teilen. Aber in diesem Fall habe ich ein neues Problem. Verweise. Der Verweis auf ein anderes Objekt muss im gesamten Diagramm aktualisiert werden. Dazu muss jedes Mal, wenn ich eine neue Grafikversion erhalte, alle Knoten besucht werden. Und dies mutiert die Knoten mit Referenzen, so dass sie auch abgeleitet werden sollten (durch Kopieren). Leistung ist nicht viel besser als Brute-Force-Kopieren.
Soweit ich mir vorstellen kann, gibt es keinen wirklich effizienten Weg, eine Mutation eines Objektgraphen mit unveränderlichen Zuständen darzustellen. Also bitte ich um eine Idee dazu.
Ist es möglich, eine Mutation eines Objektgraphen mit unveränderlichem Zustand effizient darzustellen?
quelle
Antworten:
Was Sie suchen, wird als persistente Datenstruktur bezeichnet. Die kanonische Ressource für persistente Datenstrukturen ist Chris Okasakis Buch Purely Functional Data Structures . Persistente Datenstrukturen haben in jüngster Zeit aufgrund ihrer Popularisierung in Clojure und Scala Interesse geweckt.
Aus irgendeinem seltsamen Grund scheinen persistente Grafiken jedoch größtenteils ignoriert zu werden. Wir haben Listen, Dutzende verschiedener Arten von Bäumen, Arrays, Prioritätswarteschlangen, Karten, aber keine Grafiken.
Ich habe wirklich nur ein Papier gefunden: Vollpersistente Grafiken - welche zur Auswahl?
quelle
Wenn Sie die Verbindungen zwischen den Objekten nicht als Teil Ihrer versionierten Ressource betrachten (und möglicherweise - in diesem Fall hilft das Folgende wahrscheinlich nicht viel), können Sie Ihre Objekte in einen Teil aufteilen, der den Konnektivitätsteil darstellt des Objekts und ein Teil, der den unveränderlichen Zustand darstellt.
Dann könnte jedes der Konnektivitäts-Unterobjekte einen Vektor der versionierten Zustände enthalten. Auf diese Weise können Sie mit einer Grafikversionsnummer arbeiten, um auf den entsprechenden unveränderlichen Status zuzugreifen.
Um zu vermeiden, dass bei jeder Aktualisierung eines bestimmten Knotens das gesamte Diagramm durchlaufen werden muss, können Sie festlegen, dass die aktuelle Version verwendet wird, wenn auf einen Knoten mit einer Versionsnummer zugegriffen wird, die größer als die aktuelle Versionsnummer des Knotens ist . Wenn der Knoten dann aktualisiert wird, füllen Sie alle Zwischenversionen mit der vorherigen Version aus, sodass Sie das Objektdiagramm verzögert aktualisieren können.
Wenn die Konnektivität zwischen Objekten zu Ihrem versionierten Status gehört, funktioniert das oben Gesagte nicht. Aber vielleicht können Sie es wie folgt erweitern:
Erstellen Sie für jedes Objekt im Diagramm ein "Handle-Objekt". Das Handle-Objekt enthält die Liste der versionierten unveränderlichen Zustände. Anstatt Objektreferenzen in einem der Objekte des Diagramms zu speichern, würden Sie eine Referenz auf das Handle-Objekt speichern. Dann werden alle Verweise auf die Objekte über das Handle unter Verwendung des Handles und der Versionsnummer der Ansicht des aktuell verarbeiteten Objektgraphen dereferenziert. Dies würde den korrekten unveränderlichen Zustand für das Objekt zurückgeben. Die unveränderlichen Zustände verwenden die Ziehpunkte, um auf andere Objekte im Diagramm zu verweisen, sodass Sie immer das konsistente Datum für die Version des Diagramms erhalten, das Sie verarbeiten möchten.
Die oben beschriebene Logik gilt auch für die Aktualisierung der Versionen in den Handles. Dies ermöglicht verzögerte, lokalisierte Aktualisierungen.
quelle
Es gibt eine veröffentlichte Lösung für dieses Problem mit einer sehr guten Komplexität der Amortisationszeit, aber es ist schwer zu finden, wenn Sie nicht genau wissen, wonach Sie suchen sollen. Eine schöne Zusammenfassung finden Sie in diesen Vorlesungsunterlagen (siehe Abschnitt 2.2.3) oder lesen Sie das Originalpapier Datenstrukturen persistent machen . Wenn Ihr Objektgraph nur eine eingeschränkte Konnektivität aufweist (z. B. baumartige Strukturen), können Sie sogar eine amortisierte O (1) -Komplexität erreichen, um den unveränderlichen Graphen zu lesen und zu aktualisieren, was beeindruckend ist.
Die Idee ist, dass jedes Objekt neben dem Speichern seines aktuellen unveränderlichen Zustands Platz zum Aufzeichnen von Änderungen reserviert. Wenn Sie ein neues unveränderliches Diagramm aus einem vorhandenen Diagramm durch Anwenden von Änderungen erstellen möchten, müssen Sie zwei Fälle berücksichtigen:
Das Objekt, das Sie ändern möchten, hat noch Platz für Änderungen. Sie können die Änderungen direkt im Objekt speichern.
Das Objekt hat keinen Platz mehr für Änderungen. Sie erstellen eine neue Instanz basierend auf den aktuellen Werten und leeren Änderungssätzen. Jetzt müssen Sie alle Objekte, die auf das alte Objekt verweisen, rekursiv aktualisieren, um auf das neue Objekt zu verweisen.
Wenn das referenzierende Objekt selbst noch Platz für Änderungen hat, können Sie die Referenzänderung direkt speichern, ansonsten kaskadiert es rekursiv.
Dieses Schema unterstützt zwar die effiziente Erstellung neuer Generationen unveränderlicher Objektgraphen, erschwert jedoch das Lesen, da Sie jetzt angeben müssen, auf welche "Version" Sie zugreifen möchten, wenn Sie Daten von einem unveränderlichen Objekt lesen. Dies liegt daran, dass das unveränderliche Objekt aufgrund der gespeicherten Änderungsdatensätze möglicherweise Informationen für mehrere Versionen enthält. Sie müssen daher angeben, welche Version Sie anzeigen möchten.
Bei der Implementierung versuche ich, diese Komplexität in der API zu verbergen. Wenn ich von außen auf etwas im unveränderlichen Diagramm verweise, verwende ich generierte Stichleitungen anstelle von direkten Verweisen, damit Anrufer die gewünschte Version nicht manuell weitergeben müssen. Dies macht Verweise etwas teurer als ein direkter Zeiger, aber ich finde es die Bequemlichkeit wert.
quelle