差分最適化

差分最適化

このガイドでは、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等価性に挟まれた編集をシフトして、そのうちの一つを除去します
EditOperationsOrderEnum は共有位置で DeleteFirst または InsertFirst のどちらが出力されるかを制御します

参照

 日本語