2010-11-29 4 views
1

У меня есть Dictionary<Predicate<double>, SomeEnum>:Как звонить словарь <K, V> .TryGetValue(), где K: Predicate <T>, V: перечисление

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; 

Есть ли другие способы?

Или как правильно реализовать мою идею? - объявить ключ не как константу, а как сегмент.

+8

Это не то, как использовать хэш-таблицу. – leppie 2010-11-29 20:03:15

+0

@leppie: Согласен. В моем случае это просто «Список >» (я не могу использовать 4.0 и `List >`) - – abatishchev 2010-11-29 20:11:52

+0

Это не то, как использовать lambdas. – 2010-11-29 20:12:47

ответ

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; 
} 
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? вместо SomeStruct, кроме того, поскольку то FirstOrDefault даст однозначный результат, если он не соответствует.

Если ваш список очень длинный, вам нужно будет рассмотреть некоторую структуру данных, которая разрешает запросы в диапазоне, например Interval Tree.

0

Вам придется пройти через ваши критерии и запустить каждый предикат против ввода, чтобы узнать, совпадает ли он. Я не вижу причин использовать словарь здесь.

2

Этого нельзя сделать с помощью словаря, потому что он полагается на хэш-значения, чтобы быстро определить, где искать определенный ключ.

Как вы обнаружили, вы можете напрямую вызвать предикаты, но для этого потребуются функции O (n), которые не лучше, чем использование List или даже большой оператор if/then/else.

Если ваша коллекция потенциальных предикатов слишком длинна, чтобы это было вариантом, вам необходимо создать свою собственную структуру данных, чтобы удовлетворить ваши цели. Если вы только планируете определять значения на основе целых диапазонов, это не должно быть сложно, но это может выйти из-под контроля, если ваши предикаты становятся более сложными.

На стороне примечание, язык F #, который имеет встроенную поддержку для такого определения, используя Match Expressions. Я не знаю, как это происходит с компиляцией ветвей, но я полагаю, что это довольно умно.

Редактировать

Вот пример использования сшить выражение в 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 

I» вы никогда не работали с F # раньше, поэтому вам придется искать, как использовать функцию из F # в программе на C#.

Смежные вопросы