如何在不知道密钥的情况下从F#Map获取第一个key,value pair
?
我知道Map类型用于获取给定密钥的相应值,例如find
我也知道可以将地图转换为列表并使用List.Head,例如
List.head (Map.toList map)
我想这样做
1.没有钥匙
2.不知道键的类型和值
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#map可能优于列表。我还没有决定最终的解决方案,但是想要在地图上探索这个问题,而它仍处于最前沿。
目前我正在使用:
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.Value
3>将KeyValuePair的输出按到一个元组中
答案 0 :(得分:4)
我认为没有一个完全满足您所有要求的答案,但是:
您只需使用m |> Seq.head
访问第一个键值对即可。与将地图转换为列表不同,这是懒惰的。这并不保证你总是得到相同的第一个元素,但实际上,实现将保证(它可能会在下一个版本中改变)。
为了找到最小值,您实际上并不需要保证Seq.head
始终返回相同的元素。它只需要给你一些元素。
您可以在其答案中提及其他基于Seq
的功能,如@marklam。
您还可以使用fold
状态option<'K * 'V>
,您可以使用None
进行初始化,然后您不必担心找到第一个元素:< / p>
m |> Map.fold (fun st k2 v2 ->
match st with
| Some(k1, v1) when v1 < v2 -> st
| _ -> Some(k2, v2)) None
答案 1 :(得分:3)
Map
实施IEnumerable<KeyValuePair<_,_>>
,因此您可以将其视为Seq
,例如:
let findmin (map : Map<_,_>) = map |> Seq.minBy (fun kvp -> kvp.Key)
答案 2 :(得分:1)
比其他答案更简单。 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
。