我有一个嵌套字典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
答案 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%。