繁体   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