Оптимизация diff

Оптимизация diff

Оптимизация диффа

Это руководство показывает, как очистить необработанный diff — список DiffOperation значений (см Diff как они формируются) — в каноническую, минимальную форму, используя проходы постобработки в DiffOptimization подпространстве. Каждый проход реализует IDiffOptimizationOperation и изменяет список diff на месте, поэтому проходы можно цепочкой выполнять последовательно, постепенно упрощая результат.


Контракт оптимизации

IDiffOptimizationOperation определяет единственный метод, Execute(diffs), который нормализует изменяемый список значений DiffOperation на месте, сохраняя исходный и конечный тексты, которые представляет дифф. Каждый оптимизатор в этом подпакете реализует этот интерфейс, поэтому их можно вызывать взаимозаменяемо или комбинировать в конвейере.

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 объединяет дифф в его каноническую минимальную форму: он сводит вместе смежные последовательности одного типа операций, а для смешанной последовательности удалений/вставок выделяет общий префикс в предшествующее равенство и общий суффикс в последующее равенство.

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

Устранение коротких равенств

MergingOptimizer выполняет семантический проход очистки: он удаляет равенства, не превышающие по размеру окружающие их правки, сворачивая эти короткие общие участки обратно в соседние delete/insert, а затем повторно объединяет результат в каноническую форму.

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

Перемещение правок через равенства

OperationsSlideMerger сдвигает отдельную правку, окружённую с обеих сторон равенствами, в сторону, устраняя одно из этих равенств и дальше канонизируя diff.

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

Контроль порядка delete/insert

Когда для одной и той же позиции выдаются операции delete и insert, перечисление 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?

Это означает, что соседние операции одного типа были объединены, а любой общий префикс/суффикс в смешанных блоках удаления/вставки был перенесён в соседнее равенство, так что последовательность не содержит избыточной фрагментации.

Обязательно ли использовать все три оптимизатора?

Нет. IDiffOptimizationOperation позволяет применять только необходимые проходы, но запуск OperationsMerger перед семантическими проходами (MergingOptimizer, OperationsSlideMerger) дает наиболее последовательные результаты.

В чём разница между OperationsMerger и MergingOptimizer?

OperationsMerger выполняет структурное объединение (последовательности одного типа, общий префикс/суффикс). MergingOptimizer идёт дальше и сворачивает короткие равенства, окружённые правками, обратно в соседнее удаление/вставку перед повторным слиянием.

Меняет ли EditOperationsOrder то, что представляет diff?

Нет. Он лишь контролирует порядок выдачи удаления и вставки, которые применяются к одной и той же позиции — результирующая реконструкция текста не изменяется.


API Reference Сводка

Класс / МетодОписание:
IDiffOptimizationOperationИнтерфейс для постобработки, которая нормализует diff на месте
IDiffOptimizationOperation.Execute(diffs)Запускает проход оптимизации над изменяемым списком значений DiffOperation
OperationsMergerОбъединяет соседние однотипные операции и выделяет общий префикс/суффикс в равенства
MergingOptimizerУдаляет короткие равенства, окружённые правками, затем повторно объединяет в каноническую форму
OperationsSlideMergerСдвигает правку, окружённую равенствами, чтобы устранить одно из них
EditOperationsOrderПеречисление, контролирующее, будет ли DeleteFirst или InsertFirst выведено в общей позиции

См. также:

 Русский