Diff-Optimierung

Diff-Optimierung

Dieser Leitfaden zeigt, wie man einen rohen Diff — eine Liste von DiffOperation Werten (siehe Diff wie diese erstellt werden) — in eine kanonische, minimale Form mittels der post-processing passes im DiffOptimization sub-namespace. Jeder Pass implementiert IDiffOptimizationOperation und mutiert die Diff-Liste an Ort und Stelle, sodass Passes in einer Sequenz verkettet werden können, um das Ergebnis schrittweise zu vereinfachen.


Der Optimierungsvertrag

IDiffOptimizationOperation definiert eine einzige Methode, Execute(diffs), die eine veränderbare Liste von DiffOperation-Werten vor Ort normalisiert, während die Quell- und Zieltexte, die das Diff darstellt, erhalten bleiben. Jeder Optimierer in diesem Unternamespace implementiert dieses Interface, sodass er austauschbar aufgerufen oder zu einer Pipeline zusammengesetzt werden kann.

List<DiffOperation> diffs = new List<DiffOperation>
{
    new DiffOperation(Operation.Equal, "The quick "),
    new DiffOperation(Operation.Delete, "brown "),
    new DiffOperation(Operation.Insert, "red "),
    new DiffOperation(Operation.Equal, "fox")
};

IDiffOptimizationOperation optimizer = new OperationsMerger(EditOperationsOrder.DeleteFirst);
optimizer.Execute(diffs);

Zusammenführen benachbarter Operationen

OperationsMerger fügt ein Diff zu seiner kanonischen Minimalform zusammen: Es vereinigt benachbarte Abschnitte derselben Operationsart, und bei einer gemischten delete/insert-Sequenz wird jeder gemeinsame Präfix in die vorherige equality und jeder gemeinsame Suffix in die nachfolgende equality überführt.

var merger = new OperationsMerger(EditOperationsOrder.DeleteFirst);
merger.Execute(diffs);

Eliminieren kurzer equalities

MergingOptimizer führt einen semantischen Aufräumdurchlauf durch: Es eliminiert Gleichheiten, die nicht größer sind als die sie umgebenden Änderungen, faltet diese kurzen gemeinsamen Sequenzen zurück in das angrenzende Löschen/Einfügen und fügt das Ergebnis anschließend wieder zu einer kanonischen Form zusammen.

var semanticOptimizer = new MergingOptimizer(EditOperationsOrder.InsertFirst);
semanticOptimizer.Execute(diffs);

Verschieben von Änderungen über Gleichheiten

OperationsSlideMerger verschiebt eine einzelne Änderung, die beidseitig von Gleichheiten umgeben ist, seitlich, eliminiert eine dieser Gleichheiten und kanonisiert den Diff weiter.

IDiffOptimizationOperation slideMerger = new OperationsSlideMerger();
slideMerger.Execute(diffs);

Steuerung der Reihenfolge von Löschen/Einfügen

Wenn für dieselbe Position ein Löschen und ein Einfügen ausgegeben werden, steuert das EditOperationsOrder-Enum, welches davon ein Optimierer zuerst in die resultierende Operationssequenz setzt: DeleteFirst oder InsertFirst.

var order = EditOperationsOrder.DeleteFirst;
var optimizer = new MergingOptimizer(order);
optimizer.Execute(diffs);

Tipps und bewährte Vorgehensweisen

  • Führen Sie OperationsMerger zuerst aus, um rohe Änderungen zusammenzufassen, bevor die semantischen Durchläufe angewendet werden — MergingOptimizer und OperationsSlideMerger gehen beide davon aus, dass benachbarte Operationen gleicher Art bereits kombiniert wurden.
  • Wählen Sie einen EditOperationsOrder-Wert und verwenden Sie ihn konsequent in allen Optimierern einer Pipeline; das Mischen von DeleteFirst und InsertFirst zwischen Durchläufen kann die von einem vorherigen Durchlauf festgelegte Reihenfolge rückgängig machen.
  • Alle drei Optimierer ändern das List<DiffOperation> an Ort und Stelle — klonen Sie die Liste zuerst, wenn Sie das nicht optimierte Diff zum Vergleich behalten müssen.
  • Da jeder Optimierer IDiffOptimizationOperation implementiert, können Sie eine Pipeline als IDiffOptimizationOperation[] halten und Execute in einer Schleife für jeden aufrufen, anstatt jeden Durchlauf namentlich zu hardcoden.
  • Wenden Sie OperationsSlideMerger nach den Merge-Durchläufen an — das Verschieben einer Edit ist nur nützlich, wenn benachbarte Abschnitte derselben Art bereits zusammengeführt wurden.

Häufige Probleme

ProblemUrsacheLösung
Diff enthält noch winzige fragmentierte GleichheitenNur OperationsMerger wurde ausgeführt; kurze Gleichheiten zwischen Bearbeitungen wurden nie zusammengefasstFühre außerdem MergingOptimizer nach OperationsMerger aus
Die Reihenfolge von Löschen/Einfügen wechselt unerwartet zwischen DurchläufenVerschiedene EditOperationsOrder-Werte wurden an verschiedene Optimierer übergebenVerwende denselben EditOperationsOrder-Wert für jeden Optimierer in der Pipeline
Der Optimierer scheint keine Wirkung zu habenDie Diff-Liste war bereits in kanonischer Form, oder eine Kopie der Liste wurde optimiert anstatt der OriginalreferenzBestätigen Sie, dass Sie dieselbe List<DiffOperation>-Referenz übergeben, die der nachgelagerte Code liest

FAQ

Was bedeutet „kanonische Form“ für ein Diff?

Das bedeutet, dass benachbarte Operationen derselben Art zusammengeführt wurden und jeder gemeinsame Präfix/Suffix in gemischten Lösch-/Einfügeabschnitten in die benachbarte Gleichheit integriert wurde, sodass die Sequenz keine redundante Fragmentierung mehr enthält.

Muss ich alle drei Optimierer verwenden?

Nein. IDiffOptimizationOperation ermöglicht es Ihnen, nur die erforderlichen Durchläufe anzuwenden, aber das Ausführen von OperationsMerger vor den semantischen Durchläufen (MergingOptimizer, OperationsSlideMerger) liefert die konsistentesten Ergebnisse.

Was ist der Unterschied zwischen OperationsMerger und MergingOptimizer?

OperationsMerger führt strukturelles Zusammenfassen durch (Läufe gleichen Typs, gemeinsamer Präfix/Suffix). MergingOptimizer geht noch weiter und faltet kurze Gleichheiten, die von Änderungen umgeben sind, zurück in das angrenzende Löschen/Einfügen, bevor sie erneut zusammengeführt werden.

Ändert EditOperationsOrder, was das Diff repräsentiert?

Nein. Es steuert nur die Ausgabereihenfolge eines Löschens und eines Einfügens, die auf dieselbe Position angewendet werden — die resultierende Textrekonstruktion bleibt unverändert.


API Reference Zusammenfassung

Klasse / MethodeBeschreibung
IDiffOptimizationOperationSchnittstelle für einen Nachbearbeitungsschritt, der ein Diff vor Ort normalisiert
IDiffOptimizationOperation.Execute(diffs)Führt den Optimierungsschritt über einer veränderlichen Liste von DiffOperation-Werten aus
OperationsMergerFasst benachbarte gleichartige Operationen zusammen und wandelt gemeinsamen Präfix/Suffix in Gleichungen um
MergingOptimizerEliminiert kurze Gleichheiten, die von Änderungen umgeben sind, und fügt sie anschließend wieder zu einer kanonischen Form zusammen
OperationsSlideMergerVerschiebt eine von Gleichheiten umgebene Änderung, um eine davon zu eliminieren
EditOperationsOrderEnum, das steuert, ob DeleteFirst oder InsertFirst an einer gemeinsamen Position ausgegeben wird

Siehe auch

 Deutsch