技術的な解決策には、等価比較とインデックス作成という 2 つの側面があることに注意してください。
クリフ ノーツ バージョン:
- カスタム等価比較を行うのは簡単です
- インデックス作成を実行するには、あるオブジェクトが別のオブジェクトと等しいかどうかだけでなく、どちらのオブジェクトが他のオブジェクトよりも「大きい」かを知る必要があります。
- すべてのプロパティがプリミティブである場合は、それらを単一の文字列に押しつぶし
Object
、それらを追跡するために an を使用する必要があります ( a ではありませんDictionary
)。
- 参照の等価性について個々のプロパティのいくつかを比較する必要がある場合は、どのプロパティ セットが他のプロパティ セットよりも大きいかを判断する関数を作成し、比較関数の出力を使用する独自のコレクション クラスを作成します。独自の二分探索木ベースのインデックス作成を実装します。
- 引数の一意のセットの数が数百以下で、
Object
引数の参照比較が必要な場合は、配列とsome
メソッドを使用して、キャッシュされたすべてのキーに対して単純な比較を行います。実際のメソッドがどれほど高価であるかを知っているのはあなただけなので、ルックアップ コスト (関数に提供される一意の引数の数によって異なります) を許容できるかどうかは、あなた次第です。
等価比較
等価比較に対処するには、参照の等価性ではなく、オブジェクトのプロパティの値を比較するコードを簡単に記述できます。次の関数は厳密なセット比較を強制するため、両方のオブジェクトに同じ値を持つまったく同じプロパティ (どちらのオブジェクトにもプロパティを追加することはできません) が含まれている必要があります。
public static propsEqual(obj1:Object, obj2:Object):Boolean {
for(key1:* in obj1) {
if(obj2[key1] === undefined)
return false;
if(obj2[key1] != obj2[key1])
return false;
}
for(key2:* in obj2)
if(obj1[key2] === undefined)
return false;
return true;
}
{A:1, B:2}
に等しいと見なされるトレードオフで 2 番目の for ループを削除することで、速度を上げることができます{A:1, B:2, C:'An extra property'}
。
索引付け
あなたの場合、これに関する問題は、参照の等価性を提供する、または文字列キーを提供するインデックス付けを失うことです。を使用するなど、新しい関数引数の各セットを以前に見た引数のリスト全体と比較する必要があります。フィールドとメソッドを使用して、毎回新しいクロージャを生成しないようにしています。Dictionary
Object
Array.some
currentArgs
private var cachedArgs:Array = [];
private var currentArgs:Object;
function yourMethod(stringArg:String, objArg:Object, boolArg:Boolean):* {
currentArgs = { stringArg:stringArg, objArg:objArg, boolArg:boolArg };
var iveSeenThisBefore:Boolean = cachedArgs.some(compareToCurrent);
if(!iveSeenThisBefore)
cachedArgs.push(currentArgs);
}
function compareToCurrent(obj:Object):Boolean {
return someUtil.propsEqual(obj, currentArgs);
}
これは、比較が O(n) 回になることを意味します。ここで、n は関数引数の一意のセットの増加し続ける数です。
関数のすべての引数がプリミティブである場合は、よく似た質問In AS3, where do you draw the line between Dictionary and ArrayCollection? を参照してください。. タイトルはあまり似ていないように聞こえますが、受け入れられた回答の解決策(はい、私が書きました)は、まったく同じ技術的な問題に対処しています-複数のプリミティブ値を単一の複合キーとして使用しています。あなたの場合の基本的な要点は次のとおりです。
private var cachedArgs:Object = {};
function yourMethod(stringArg:String, objArg:Object, boolArg:Boolean):* {
var argKey:String = stringArg + objArg.toString() + (boolArg ? 'T' : 'F');
if(cachedArgs[argKey] === undefined)
cachedArgs[argKey] = _yourMethod(stringArg, objArg, boolArg);
return cachedArgs[argKey];
}
private function _yourMethod(stringArg:String, objArg:Object, boolArg:Boolean):* {
// Do stuff
return something;
}
どの参照が別の参照よりも「大きい」かを本当に判断する必要がある場合 (Dictionary
が内部的に行うように)、Adobe は「値」/「アドレス」を取得するための API をまだ提供していないため、いくつかの醜いものに足を踏み入れる必要があります。 "参照の。私がこれまでに見つけた最高のものは、この興味深いハックです: How can I get an instance's "memory location" in ActionScript? . 一連のパフォーマンス テストを行わないと、このハックを使用して参照を比較すると、バイナリ サーチ ツリーの indexnig によって得られる利点が失われるかどうかわかりません。当然、キーの数によって異なります。