1

私は持っていますDictionary<Predicate<double>, SomeEnum>

var dic = new Dictionary<Predicate<double>, SomeEnum>
{
    { (d) => d < 10, SomeEnum.Foo },
    { (d) => d > 90, SomeEnum.Bar }
};

私はこのようにそれに対して呼び出したいTryGetValue(K, out V):

dic.TryGetValue(99)

そして受け取る

SomeStruct.Bar

しかし、の最初のパラメータTryGetValue()Predicate<T>、だけではありませんT。どうすればやりたいことができますか?

汚い回避策しか見つかりませんでした:

var kpv = dic.FirstOrDefault(p => p.Key(99));
if (kpv.Key != null)
    var result = kpv.Value;

他の方法はありますか?

または、私のアイデアを適切に実装する方法は?- キーを定数としてではなく、セグメントのように宣言します。

4

4 に答える 4

3

ここで間違っていることがいくつかあります。

Predicate<double>として使用するのに適切なタイプではありませんTKey。辞書のキーは、値を計算するのではなく、値を識別することになっています。

これは、ラムダを使用しても意味がありません。それらは匿名であるため、同等のものは得られず、辞書を使用することもできません。

説明については、次のコードサンプルを参照してください。

Predicate<double> fn_1 = d => d == 34.0d;
Predicate<double> fn_2 = d => d == 34.0d;

// Note: There are not equal
if (fn_1 == fn_2)
    Console.WriteLine("These are Equal?");

どちらかといえば、デリゲートのリストを使用してそれぞれを実行し、一致するものを見つけることができますが、その時点で複数の結果を期待する必要があります。単一の結果のみを取得したい場合は、述語がリスト内に格納されている順序を検討する必要があります。

KeyValuePair持っていないためのハックとして誤用しないでくださいTuple<T1,T2>。PredicateとSomeStructの両方を持つクラスを作成するのはかなり簡単です。見て:

public class MySegment
{   
     public Predicate<double> Predicate {get;set;}
     public SomeStruct Result {get;set;}
}

一連の述語を調べて、一致する述語を見つけるには、次のようになります。

...
List<MySegment> list = new List<MySegment>();
...
list.Add(new MySegment { Predicate = d => d < 10, Result = SomeStruct.Foo });
list.Add(new MySegment { Predicate = d => d > 90, Result = SomeStruct.Bar });

...

public IEnumerable<SomeStruct> GetResults(double input)
{ 
    foreach (var item in list)
        if (item.Predicate(input))
             yield return item.Result;
}
于 2010-11-29T21:10:39.367 に答える
2

これは Dictionary を使用して行うことはできません。これは、特定のキーを探す場所をすばやく決定するためにハッシュ値に依存しているためです。

あなたが発見したように、述語を直接呼び出すことができますが、それには O(n) 関数を呼び出す必要があり、リストや大きな if/then/else ステートメントを使用するよりも優れていません。

可能性のある述語のコレクションが長すぎてこれをオプションにできない場合は、目的を満たすために独自のデータ構造を作成する必要があります。整数範囲に基づいて値を定義することのみを計画している場合、これは難しくありませんが、述語がより複雑になると手に負えなくなる可能性があります。

余談ですが、 Match Expressionsを使用したこの種の定義のサポートが組み込まれている F# 言語。ブランチをコンパイルする方法はわかりませんが、かなり賢いと思います。

編集

以下は、F# で次のようなものに一致式を使用する例です。

// Define the "choose" function
let choose value = 
    match value with
    | v when v < 10 -> 1
    | v when v > 90 -> 2
    | _ -> 0

// Test the "choose" function
let choice1 = choose 5
let choice2 = choose 15
let choice3 = choose 95

上記のコードは、次の値を生成します。

choice1 = 1 
choice2 = 0 
choice3 = 2

私はこれまで実際に F# を扱ったことがないので、C# プログラムで F# の関数を使用する方法を調べてみる必要があります。

于 2010-11-29T20:25:37.187 に答える
2

述語のリストが長すぎない場合は、それらを に追加してからList<KeyValuePair<Predicate<T>, V>>、LINQ クエリを実行できます。

var lt10 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d < 10, SomeStruct.Foo);
var gt90 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d > 90, SomeStruct.Bar);
var predicates = new List<KeyValuePair<Predicate<Double>, SomeStruct>>() { lt10, gt90 };

var result = predicates.FirstOrDefault(p => p.Key(99));

さらに、SomeStruct?の代わりに を使用した方がよいでしょう。これは、一致しない場合に明確な結果が得られるためです。SomeStructFirstOrDefault

リストが非常に長い場合は、 Interval Treeのように、範囲に対するクエリを許可するある種のデータ構造を検討する必要があります。

于 2010-11-29T20:16:04.300 に答える
0

条件をループし、入力に対して各 Predicate を実行して、一致するかどうかを確認する必要があります。ここで Dictionary を使用する理由はありません。

于 2010-11-29T20:17:49.873 に答える