F#System.InvalidOperationException:集合被修改;枚举操作可能无法执行

时间:2016-04-22 02:40:23

标签: list dictionary f#

我在F#遇到这个问题 [不是C#,其中已经有一个类似的帖子有类似的答案]

我理解在for循环中枚举它时不可能修改Dictionary 我该怎么回事?

let edgelist1 = [(1,2,3.0f);(1,2,4.0f);(5,6,7.0f);(5,6,8.0f)]
let dict_edges = new Dictionary<int*int,(int*int*float32) list>()
for x in edgelist1 do dict_edges.Add ((fun (a,b,c)-> (a,b)) x, x)
for k in dict_edges.Keys do dict_edges.[k] <- (dict_edges.[k] |> List.rev)
  

System.InvalidOperationException:修改了集合;列举   操作可能无法执行。

     
    

在System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource     资源)     System.Collections.Generic.Dictionary`2.KeyCollection.Enumerator.MoveNext()     at。$ FSI_0101.main @()

  

单独这是有效的

dict_edges.[(1,2)] <- dict_edges.[(1,2)] |> List.rev;;
在for循环中我只需要更改字典值,而不是键。

感谢

3 个答案:

答案 0 :(得分:5)

您可以将所有密钥复制到临时列表中,然后在修改原始字典时迭代该列表:

for k in (dict_edges.Keys |> Seq.toList) do 
   dict_edges.[k] <- (dict_edges.[k] |> List.rev)

但我强烈建议你重新思考一下你的方法并摆脱就地变异。您现在面临的这个小问题只是对基于突变的程序可能出错的第一个品味。

答案 1 :(得分:4)

你发布的代码甚至在语法上都不正确,所以不清楚你想要实现的是什么(编译器尖叫((fun (a,b,c)-> (a,b)) x, x)说它希望第二个x是一个列表)< / p>

我猜你所追求的是:你有一个加权边的列表,其中节点之间可以有多个边。您希望将它们折叠为规范形式,您可以将所有边缘分组,以连接任何节点对(i,j)。只需使用任何groupBy库函数,就可以了:

let map_edges =
    edgelist1
    |> List.groupBy (fun (a, b, _) -> (a, b))
    |> Map.ofList

在当前代码中,您使用((fun (a,b,c)-> (a,b)) x, x)来提取元组的成员。相反,请在for表达式中使用模式:

for (a, b, c) in edgelist1 do dict_edges.Add ((a, b), [(a, b, c)])

(我已添加[]以使其至少编译)

另请注意,您正在复制信息:将节点元组存储在键和列表的值中,使数据结构可能不一致且更大。请考虑以下事项:

let map_edges =
    edgelist1
    |> List.map (fun (a, b, c) -> (a, b), c)
    |> List.groupBy fst
    |> List.map (fun (nodeTuple, edgeList) -> 
        nodeTuple, (edgeList |> List.map snd))
    |> Map.ofList


map_edges
|> Map.iter (fun (nodeI, nodeJ) edgeList ->
    edgeList
    |> Seq.map string
    |> String.concat "; "
    |> printfn "Nodes (%i, %i): weights %s" nodeI nodeJ
)

(您可能希望使用序列作为中间表示而不是列表)

答案 2 :(得分:1)

仅仅说dict_edges = dict_edges.map($ 0.reverse())

会不会更容易

抱歉f#语法错误