2013-12-02 2 views
3

Я хочу создать функцию F #, такую ​​как List.find, но вместо того, чтобы искать одно значение, я хочу найти любой из ключей словарь и вернуть соответствующее значение словаря. Например, это (бедная) реализация того, что я пытаюсь сделать.F # - Функция типа List.find, но поиск любого из ключей словаря

let dict1=dict[(1,"A");(2,"B");(3,"C");(4,"D");(5,"E");(6,"F")] 

let findInDict l = 
    let mutable found=false 
    let mutable value="" 
    for elem in l do 
     let f,v=dict1.TryGetValue(elem) 
     value<-if f && not found then v else value 
     found<-if not found then f else found 
    value 

findInDict [9;2;5] 


> 

val dict1 : System.Collections.Generic.IDictionary<int,string> 
val findInDict : l:seq<int> -> string 
val it : string = "B" 

Что было бы функциональным эквивалентом?

ответ

2

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

[for x in [9;4;5] do if dict1.ContainsKey x then yield dict1.[x]] 

Edit:

После перечитывания ваш вопрос, я понял, что выше было не совсем то, что вы ищете.

let rec findAValue l = 
    match l with 
    | [] -> None 
    | x::xs -> if dict1.ContainsKey x then Some(dict1.[x]) else findAValue xs 

или более лаконично:

let rec findAValue = function 
      | [] -> None 
      | x::xs -> if dict1.ContainsKey x then Some(dict1.[x]) else findAValue xs 

еще более лаконично:

let findAValue = List.tryPick (fun x-> if dict1.ContainsKey x then Some(dict1.[x]) else None) 

let highPerformanceFindAValue = List.tryPick (fun x-> match dict1.TryGetValue x with 
                 | true, value->Some(value) 
                 | _ -> None) 

В том случае, если значение не найдено результат является None в противном случае это Some(value).

+0

tryPick - это именно то, что я искал. Почувствуйте глупость, чтобы не заметить эту функцию. – dood

+0

@ user3058579, похоже, другие ответчики тоже. ;-) – mydogisbox

+0

FYI - последнее решение получает доступ к словарю дважды за каждый успешный ключ. Лучше использовать TryGetValue(). – plinth

0
let findFirst l (dict: System.Collections.Generic.Dictionary<int, string>) = 
    let o = l |> List.tryFind (fun i -> dict.ContainsKey(i)) |> Option.map (fun k -> dict.[k]) 
    match o with | None -> "" | Some(k) -> k 
0

Существует множество способов сделать это.

Очевидное решение состоит в итерацию, как вы делали:

let findInDict (d:IDictionary<'a, 'b>) l = 
    seq { 
     for key in l do 
      let f, v = d.TryGetValue(key) 
      if f then yield v 
    } 

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

Вы можете переписать это в терминах некоторых операторов последовательности:

let findInDict1 (d:IDictionary<'a, 'b>) l = 
    Seq.filter (fun elem -> d.ContainsKey(elem)) l |> Seq.map (fun elem -> d.Item(elem)) 

, который чувствует себя более функциональным, но явно делает намного больше работы, чем это должно быть.

let findInDict2 (d:IDictionary<'a, 'b>) l = 
    Seq.choose(fun elem -> 
     let f,v = d.TryGetValue(elem) 
     if f then Some(v) else None) l 

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

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