簡體   English   中英

如何在不知道鍵的情況下從 F# Map 獲取第一個鍵值對?

[英]How does one get the first key,value pair from F# Map without knowing the key?

如何在不知道鍵的情況下從 F# Map獲取第一個key,value pair

我知道 Map 類型用於獲取給定鍵的相應值,例如find

我也知道可以將地圖轉換為列表並使用 List.Head,例如

List.head (Map.toList map)

我想這樣做
1.沒有鑰匙
2.不知道key和value的類型
3.不使用可變的
4.無需遍歷整個地圖
5. 不做轉換,遍歷可見后面的整個地圖,例如 Map.toList 等。

我也知道如果一個人得到第一個key,value pair它可能沒有用,因為地圖文檔沒有說明在兩個不同的調用中使用 map 是否保證相同的順序。

如果無法編寫代碼,則可以接受來自諸如 MSDN 之類的站點的現有參考資料,用於解釋和說明原因。

TLDR;

我如何解決這個問題是轉換這個函數:

let findmin l = 
    List.foldBack
        (fun (_,pr1 as p1) (_,pr2 as p2) -> if pr1 <= pr2 then p1 else p2)
        (List.tail l) (List.head l)

它基於列表,用於在string * int的關聯列表中查找最小值。

示例列表:

["+",10; "-",10; "*",20; "/",20]

該列表用於解析具有優先級的二元運算符表達式,其中字符串是二元運算符,而 int 是優先級。 其他函數是在數據上執行的,因此使用 F# 映射可能比列表更具優勢。 我還沒有決定最終的解決方案,但想在地圖仍然處於最前沿的時候探索這個問題。

目前我正在使用:

let findmin m =
    if Map.isEmpty m then 
        None
    else
        let result = 
            Map.foldBack 
                (fun key value (k,v) -> 
                    if value <= v then (key,value) 
                    else (k,v))
                m ("",1000)
        Some(result)

但是在這里我不得不在初始狀態("",1000)進行硬編碼,而最好的方法是使用地圖中的第一個值作為初始狀態,然后將地圖的其余部分作為起始地圖進行傳遞與列表:

(List.tail l) (List.head l)

是的,這是對地圖進行分區,但這不起作用,例如,

let infixes = ["+",10; "-",10; "*",20; "/",20]
let infixMap = infixes |> Map.ofList
let mutable test = true
let fx k v : bool =
    if test then 
        printfn "first"
        test <- false
        true
    else 
        printfn "rest"
        false
let (first,rest) = Map.partition fx infixMap

這導致

val rest : Map<string,int> = map [("*", 20); ("+", 10); ("-", 10)]  
val first : Map<string,int> = map [("/", 20)]

首先是兩個映射而不是鍵值對

("/",20)

關於答案的注釋

對於優先解析的實際目的,在最終轉換中查看+操作之前-是可取的,因此返回+之前-是可取的。 因此, marklam對答案的這種變化

let findmin (map : Map<_,_>) = map |> Seq.minBy (fun kvp -> kvp.Value)

實現了這一點,並通過Tomas 進行了這種變化

let findmin m = 
    Map.foldBack (fun k2 v2 st ->
        match st with
        | Some(k1, v1) when v1 < v2 -> st
        | _ -> Some(k2, v2)) m None

Seq.head的使用確實返回map的第一項,但必須注意map是使用排序的鍵構建的,因此對於我的實際示例,我想從最低值10 ,因為這些項是key排序,返回的第一個是("*",20)*是第一個鍵,因為鍵是字符串並按此類排序。

為了讓我實際使用marklam的答案,我必須在調用之前檢查空列表,然后使用let (a,b) = kvp.Key,kvp.ValueKeyValuePair的輸出按摩到元組中

我認為沒有完全滿足您所有要求的答案,但是:

  1. 您可以使用m |> Seq.head訪問第一個鍵值對。 與將地圖轉換為列表不同,這很懶惰。 這並不能保證你總是得到相同的第一個元素,但實際上,實現將保證(盡管它可能會在下一個版本中改變)。

  2. 為了找到最小值,您實際上並不需要保證Seq.head始終返回相同的元素。 它只需要給你一些元素。

  3. 您可以使用其他基於Seq的函數作為@marklam 在他的回答中提到的。

  4. 您還可以將foldoption<'K * 'V>類型的狀態一起使用,您可以使用None進行初始化,然后您不必擔心找到第一個元素:

     m |> Map.fold (fun st k2 v2 -> match st with | Some(k1, v1) when v1 < v2 -> st | _ -> Some(k2, v2)) None

Map實現了IEnumerable<KeyValuePair<_,_>>所以你可以把它當作一個Seq ,比如:

let findmin (map : Map<_,_>) = map |> Seq.minBy (fun kvp -> kvp.Key)

它甚至比其他答案更簡單。 Map內部使用 AVL 平衡樹,因此條目已經按鍵排序。 正如@marklam Map所提到的,實現了IEnumerable<KeyValuePair<_,_>>所以:

let m = Map.empty.Add("Y", 2).Add("X", 1)
let (key, value) = m |> Seq.head
// will return ("X", 1)

元素添加到映射中的順序無關緊要, Seq.head可以直接對映射進行操作並返回最小鍵的鍵/值映射。

有時需要將 Map 顯式轉換為 Seq:

let m = Map.empty.Add("Y", 2).Add("X", 1)
let (key, value) = m |> Map.toSeq |> Seq.head

我在這種情況下看到的錯誤消息說“類型'a * 'b與類型Collections.Generic.KeyValuePair<string, int>不匹配”。 也可以添加類型注釋而不是Map.toSeq

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM