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
OperationsMergerzuerst aus, um rohe Änderungen zusammenzufassen, bevor die semantischen Durchläufe angewendet werden —MergingOptimizerundOperationsSlideMergergehen 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 vonDeleteFirstundInsertFirstzwischen 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
IDiffOptimizationOperationimplementiert, können Sie eine Pipeline alsIDiffOptimizationOperation[]halten undExecutein einer Schleife für jeden aufrufen, anstatt jeden Durchlauf namentlich zu hardcoden. - Wenden Sie
OperationsSlideMergernach 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
| Problem | Ursache | Lösung |
|---|---|---|
| Diff enthält noch winzige fragmentierte Gleichheiten | Nur OperationsMerger wurde ausgeführt; kurze Gleichheiten zwischen Bearbeitungen wurden nie zusammengefasst | Führe außerdem MergingOptimizer nach OperationsMerger aus |
| Die Reihenfolge von Löschen/Einfügen wechselt unerwartet zwischen Durchläufen | Verschiedene EditOperationsOrder-Werte wurden an verschiedene Optimierer übergeben | Verwende denselben EditOperationsOrder-Wert für jeden Optimierer in der Pipeline |
| Der Optimierer scheint keine Wirkung zu haben | Die Diff-Liste war bereits in kanonischer Form, oder eine Kopie der Liste wurde optimiert anstatt der Originalreferenz | Bestä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 / Methode | Beschreibung |
|---|---|
IDiffOptimizationOperation | Schnittstelle 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 |
OperationsMerger | Fasst benachbarte gleichartige Operationen zusammen und wandelt gemeinsamen Präfix/Suffix in Gleichungen um |
MergingOptimizer | Eliminiert kurze Gleichheiten, die von Änderungen umgeben sind, und fügt sie anschließend wieder zu einer kanonischen Form zusammen |
OperationsSlideMerger | Verschiebt eine von Gleichheiten umgebene Änderung, um eine davon zu eliminieren |
EditOperationsOrder | Enum, das steuert, ob DeleteFirst oder InsertFirst an einer gemeinsamen Position ausgegeben wird |