Оптимізація diff
Оптимізація diff
Цей посібник показує, як очистити необроблений diff — список DiffOperation значень (див. Diff для того, як вони створюються) — у канонічну, мінімальну форму за допомогою постобробних проходів у DiffOptimization підпросторі імен. Кожен прохід реалізує IDiffOptimizationOperation і змінює список diff на місці, тому проходи можна ланцюжком виконувати послідовно, поступово спрощуючи результат.
Контракт оптимізації
IDiffOptimizationOperation визначає єдиний метод, Execute(diffs), який нормалізує змінюваний список значень DiffOperation на місці, зберігаючи при цьому вихідний та цільовий тексти, які представляє diff. Кожен оптимізатор у цьому підпросторі реалізує цей інтерфейс, тому їх можна викликати взаємозамінно або комбінувати у конвеєрі.
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);Об’єднання суміжних операцій
OperationsMerger об’єднує diff у його канонічну мінімальну форму: він зливає суміжні послідовності одного типу операції, а для змішаного блоку delete/insert переносить будь-який спільний префікс у попередню рівність і будь-який спільний суфікс у наступну рівність.
var merger = new OperationsMerger(EditOperationsOrder.DeleteFirst);
merger.Execute(diffs);Видалення коротких рівностей
MergingOptimizer виконує семантичний етап очищення: він усуває рівності, які не більші за редагування, що їх оточують, підсуваючи ці короткі спільні ділянки назад у прилегле видалення/вставку, а потім знову об’єднує результат у канонічну форму.
var semanticOptimizer = new MergingOptimizer(EditOperationsOrder.InsertFirst);
semanticOptimizer.Execute(diffs);Зсув редагувань через рівності
OperationsSlideMerger зсуває окреме редагування, яке з обох боків оточене рівностями, вбік, усуваючи одну з цих рівностей і ще більше канонізуючи diff.
IDiffOptimizationOperation slideMerger = new OperationsSlideMerger();
slideMerger.Execute(diffs);Керування порядком видалення/вставки
Коли для тієї самої позиції генеруються операції видалення і вставки, перелік EditOperationsOrder визначає, яку з них оптимізатор ставить першою у отриманій послідовності операцій: DeleteFirst або InsertFirst.
var order = EditOperationsOrder.DeleteFirst;
var optimizer = new MergingOptimizer(order);
optimizer.Execute(diffs);Поради та найкращі практики
- Запустіть
OperationsMergerспочатку, щоб об’єднати необроблені редагування перед застосуванням семантичних етапів —MergingOptimizerіOperationsSlideMergerпередбачають, що суміжні операції одного типу вже об’єднані. - Виберіть одне значення
EditOperationsOrderі використовуйте його послідовно у всіх оптимізаторах конвеєра; змішуванняDeleteFirstіInsertFirstміж етапами може скасувати порядок, встановлений попереднім етапом. - Усі три оптимізатори змінюють
List<DiffOperation>на місці — спочатку клонувати список, якщо потрібно зберегти неоптимізований diff для порівняння. - Оскільки кожен оптимізатор реалізує
IDiffOptimizationOperation, ви можете зберігати конвеєр якIDiffOptimizationOperation[]і викликатиExecuteдля кожного у циклі замість жорсткого кодування кожного проходу за назвою. - Застосуйте
OperationsSlideMergerпісля проходів злиття — ковзання редагування корисне лише тоді, коли суміжні ділянки одного типу вже об’єднані.
Поширені проблеми
| Проблема | Причина | Виправлення |
|---|---|---|
| Diff все ще містить дрібні фрагментовані рівності | Був запущений лише OperationsMerger; короткі рівності між правками ніколи не згорталися | Також запустіть MergingOptimizer після OperationsMerger |
| Порядок видалення/вставки несподівано змінюється між проходами | Різні значення EditOperationsOrder передавалися різним оптимізаторам | Використовуйте одне і те ж значення EditOperationsOrder для кожного оптимізатора у конвеєрі |
| Оптимізатор, схоже, не має впливу | Список diff вже був у канонічній формі, або копія списку була оптимізована замість оригінального посилання | Підтвердьте, що ви передаєте те саме посилання List<DiffOperation>, яке читає код нижчого рівня |
FAQ
Що означає «канонічна форма» для diff?
Це означає, що суміжні операції одного типу були об’єднані, а будь-який спільний префікс/суфікс у змішаних ділянках delete/insert був перенесений у сусідню рівність, тому послідовність не містить зайвої фрагментації.
Чи потрібно використовувати всі три оптимізатори?
Ні. IDiffOptimizationOperation дозволяє застосовувати лише потрібні вам проходи, але запуск OperationsMerger перед семантичними проходами (MergingOptimizer, OperationsSlideMerger) дає найпослідовніші результати.
Яка різниця між OperationsMerger та MergingOptimizer?
OperationsMerger виконує структурне об’єднання (послідовності одного типу, загальний префікс/суфікс). MergingOptimizer йде далі і згортає короткі рівності, оточені правками, назад у прилегле видалення/вставку перед повторним злиттям.
Чи змінює EditOperationsOrder те, що представляє diff?
Ні. Він лише контролює порядок випуску видалення та вставки, які застосовуються до того ж положення — відтворення отриманого тексту не змінюється.
API Reference Підсумок
| Клас / Метод | Опис |
|---|---|
IDiffOptimizationOperation | Інтерфейс для постобробного проходу, що нормалізує diff на місці |
IDiffOptimizationOperation.Execute(diffs) | Виконує оптимізаційний прохід по змінному списку значень DiffOperation |
OperationsMerger | Об’єднує суміжні операції одного типу та виводить спільний префікс/суфікс у рівності |
MergingOptimizer | Видаляє короткі рівності, оточені правками, а потім знову об’єднує у канонічну форму |
OperationsSlideMerger | Переміщує правку, оточену рівностями, щоб усунути одну з них |
EditOperationsOrder | Enum, що контролює, чи буде виведено DeleteFirst чи InsertFirst у спільній позиції |