在f#Map<'a,Map<'b,'T>>)中反转嵌套词典 - >地图< 'B,地图<' 一'T>>

时间:2012-03-22 08:52:52

标签: dictionary f# nested-sets higher-order-functions

我有一个嵌套字典Map<'a,Map<'b,'T>>,因此对于a*b的组合,该条目是唯一的。

为了有效地预先计算,我需要在Map<'b,Map<'a,'T>>

中反转键

我有一些更高阶的方法来完成这项工作(|/>将在嵌套序列|//>中应用相同的操作,但深度为2级,|*>将枚举笛卡尔积嵌套序列),但我想知道是否有更好的方法来做到这一点,以防万一有漂亮的代码在这个上分享。

let reversenmap (x:Map<'a,Map<'b,'T>>) :Map<'b,Map<'a,'T>> = 
      let ret  = x |> Map.toSeq |/> Map.toSeq |*> squash12
      let ret2 = ret |> Seq.groupByn2 (fun (a,b,t) -> b) 
                                      (fun (a,b,t) -> a) |//> Seq.head 
                                                         |//> (fun (a,b,c) -> c)
      ret2 |> Seq.toMapn2

4 个答案:

答案 0 :(得分:7)

您尝试解决的问题称为有向图的转置,可以使用双倍折叠计算如下:

let invert xyzs =
  Map.fold (fun m x yzs ->
    Map.fold (fun m y z ->
      let m' = defaultArg (Map.tryFind y m) Map.empty
      Map.add y (Map.add x z m') m) m yzs) Map.empty xyzs

有关详细信息,请参阅F#.NET Journal文章Graph algorithms: topological sort and all-pairs shortest paths

答案 1 :(得分:5)

我认为来自@pad的解决方案肯定比使用|/>|*>这样的非标准运算符更加惯用F#。我可能更喜欢使用序列表达式而不是Seq.collect的版本,它看起来像这样(第二部分与@pad中的版本相同):

let reverse (map: Map<'a,Map<'b,'T>>) = 
  [ for (KeyValue(a, m)) in map do
      for (KeyValue(b, v)) in m do yield b, (a, v) ]
  |> Seq.groupBy fst 
  |> Seq.map (fun (b, ats) -> b, ats |> Seq.map snd |> Map.ofSeq) 
  |> Map.ofSeq 

答案 2 :(得分:2)

我不确定我理解你的意图。但是从你的功能的签名,我们可以做这样的事情:

let reverse (map: Map<'a,Map<'b,'T>>) =
    map |> Seq.collect (fun (KeyValue(a, m)) -> 
                                m |> Seq.map (fun (KeyValue(b, t)) -> b, (a, t)))
        |> Seq.groupBy fst
        |> Seq.map (fun (b, ats) -> b, ats |> Seq.map snd |> Map.ofSeq)
        |> Map.ofSeq

答案 3 :(得分:2)

@pad的解决方案与我提出的解决方案非常相似 - 我想它只是表明,在遇到这些问题时,你会跟着自己的鼻子做一些可行的事情,直到你到达那里。

或者,如果你想坚持折叠,你可以这样做:

let invertNesting ( map : Map<'a, Map<'b, 'c>> ) =
    let foldHelper ( oldState : Map<'b, Map<'a, 'c>> ) ( k1 : 'a ) ( innerMap : Map<'b, 'c> =
        innerMap |> Map.fold (fun tempState k2 v ->
                                  let innerMap' = match ( tempState |> Map.tryFind k2 ) with
                                                  | Some(m) -> m
                                                  | None -> Map.empty
                                  let innerMap'' = innerMap' |> Map.add k1 v
                                  tempState |> Map.add k2 innerMap'' ) oldState
    map |> Map.fold foldHelper Map.empty

虽然@Tomas Petricek的解决方案对我来说更具可读性,但这似乎要快25%。