差分最適化
差分最適化
このガイドでは、raw diff — 値のリスト — をクリーンアップする方法を示します DiffOperation 値(参照 Diff それらがどのように構築されるか) — を、サブ名前空間のポストプロセッシングパスを使用して、標準的かつ最小限の形に変換します the DiffOptimization sub-namespace。各パスは実装します IDiffOptimizationOperation そして diff リストをその場で変更するため、パスはシーケンスで連結でき、結果を段階的に単純化できます。
最適化契約
IDiffOptimizationOperation は単一のメソッド Execute(diffs) を定義し、diff が表すソーステキストとデスティネーションテキストを保持しながら、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 は 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を混在させると、前のパスが確立した順序が解除される可能性があります。 - 3つのオプティマイザはすべて
List<DiffOperation>をその場で変更します — 比較のために最適化されていない diff を保持したい場合は、まずリストをクローンしてください。 - すべてのオプティマイザが
IDiffOptimizationOperationを実装しているので、パイプラインをIDiffOptimizationOperation[]として保持し、名前で個別にハードコーディングする代わりにループ内で各オプティマイザに対してExecuteを呼び出すことができます。 - マージパスの後に
OperationsSlideMergerを適用します — 同種の隣接するランがすでに統合されている場合にのみ、編集のスライドは有用です。
一般的な問題
| 問題 | 原因 | 修正 |
|---|---|---|
| Diff にはまだ小さな断片的等価が含まれています | 実行されたのは OperationsMerger のみで、編集間の短い等価は決して折りたたまれませんでした | また、OperationsMerger の後に MergingOptimizer を実行してください |
| 削除/挿入の順序がパス間で予期せず入れ替わります | 異なる EditOperationsOrder の値が異なるオプティマイザに渡されました | パイプライン内のすべてのオプティマイザに同じ EditOperationsOrder の値を使用してください |
| オプティマイザは効果がないようです | diff リストはすでに標準形であったか、リストのコピーが元の参照ではなく最適化されました | 下流コードが読み取る同じ List<DiffOperation> 参照を渡していることを確認してください |
FAQ
diff の「標準形」とは何を意味しますか?
これは、同種の隣接する操作が統合され、削除/挿入が混在するランにおける共通の接頭辞/接尾辞が隣接する等価部分に折り込まれることを意味し、結果としてシーケンスに冗長な断片化がなくなる、ということです。
3つのオプティマイザすべてを使用しなければならないですか?
No. IDiffOptimizationOperation は必要なパスだけを適用できますが、セマンティックパス (MergingOptimizer、OperationsSlideMerger) の前に OperationsMerger を実行すると、最も一貫した結果が得られます。
OperationsMerger と MergingOptimizer の違いは何ですか?
OperationsMerger は構造的な統合(同種のラン、共通の接頭辞/接尾辞)を行います。MergingOptimizer はさらに一歩進んで、編集に囲まれた短い等式を隣接する削除/挿入に折り返し、再マージする前に統合します。
EditOperationsOrder は diff の表す内容を変更しますか?
いいえ。同じ位置に適用される削除と挿入の出力順序だけを制御し、結果として得られるテキストの再構築には影響しません。
API Reference 概要
| クラス / メソッド | 説明 |
|---|---|
IDiffOptimizationOperation | 差分をその場で正規化するポストプロセスパスのインターフェース |
IDiffOptimizationOperation.Execute(diffs) | 可変リストの DiffOperation 値に対して最適化パスを実行します |
OperationsMerger | 隣接する同種の操作を統合し、共通の接頭辞/接尾辞を等式にまとめます |
MergingOptimizer | 編集に挟まれた短い等価性を除去し、正規形に再マージします |
OperationsSlideMerger | 等価性に挟まれた編集をシフトして、そのうちの一つを除去します |
EditOperationsOrder | Enum は共有位置で DeleteFirst または InsertFirst のどちらが出力されるかを制御します |