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 |