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 合并为其规范的最小形式:它会将相同操作类型的相邻运行合并,对于混合的删除/插入运行,它会将共同前缀提取到前面的等价段,将共同后缀提取到后面的等价段。

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

消除短等价段

MergingOptimizer 执行语义清理过程:它消除不大于其周围编辑的等价块,将这些短的公共序列折回相邻的删除/插入中,然后将结果重新合并为规范形式。

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

在等价块上滑动编辑

OperationsSlideMerger 将两侧被等价块包围的单个编辑横向移动,消除其中一个等价块,进一步对差异进行规范化。

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;编辑之间的短相等从未被折叠在OperationsMerger之后也运行MergingOptimizer
删除/插入顺序在不同遍之间意外翻转不同的EditOperationsOrder值被传递给不同的优化器在管道中的每个优化器使用相同的EditOperationsOrder值
优化器似乎没有任何效果diff列表已经是规范形式,或者对列表的副本进行了优化,而不是原始引用确认您传递的 List<DiffOperation> 引用与下游代码读取的相同

FAQ

在 diff 中,“规范形式”是什么意思?

这意味着相同类型的相邻操作已被合并,并且在混合的删除/插入序列中任何公共的前缀/后缀都已折叠到相邻的相等块中,从而使序列不再出现冗余的碎片化。

我必须使用全部三个优化器吗?

No. IDiffOptimizationOperation 让您只应用所需的遍历,但在语义遍历 (MergingOptimizer, OperationsSlideMerger) 之前运行 OperationsMerger 能获得最一致的结果。

OperationsMerger 和 MergingOptimizer 有什么区别?

OperationsMerger 执行结构性合并(相同类型的运行,共同前缀/后缀)。MergingOptimizer 更进一步,在重新合并之前,将被编辑包围的短等式折叠回相邻的删除/插入。

EditOperationsOrder 会改变 diff 所表示的内容吗?

不。它仅控制在相同位置上删除和插入的发射顺序——结果文本的重建不受影响。


API Reference 摘要

类 / 方法描述
IDiffOptimizationOperation用于在原位规范化 diff 的后处理阶段的接口
IDiffOptimizationOperation.Execute(diffs)在可变的 DiffOperation 值列表上运行优化阶段
OperationsMerger将相邻的相同类型操作合并,并将共同的前缀/后缀提炼为等式
MergingOptimizer消除被编辑包围的短等式,然后重新合并为规范形式
OperationsSlideMerger将被等式包围的编辑移动,以消除其中一个等式
EditOperationsOrder枚举用于控制在共享位置是输出 DeleteFirst 还是 InsertFirst

另请参阅

 中文