F#

时间:2015-11-10 04:28:10

标签: list f#

所以我有一个问题是如何折叠两个列表来生成一个新列表(或数组,或序列或最快的基本数据类型),因为我使用的是List.Fold和::运营商建立一个列表,当我意识到我基本上生产了十亿个不可变列表...这似乎比传统的循环更慢,我想知道我在做什么是解决我的“正确”方法F#中的问题......

这是我到目前为止所得到的。我正在尝试构建一个结构为Dictionary<string,Node>*Edge list的图形,其输入是节点列表和边缘列表。每个节点都有一个插槽用于邻接列表(我不喜欢这些列表,我只是不知道哪种数据类型最好......)边缘(所以我只需要查找一个节点)。我的build_graph正在尝试获取节点列表和边缘列表,并搜索每个节点的所有边缘,并构建一个新的节点列表,其中填充了邻接列表......

所以这里是代码......(试图将它分隔开以使得线条不够长)

let build_graph (nodes:Node<'a> list) (edges:Edge<'a> list) (g:Graph<'a>) = 
    let nlist = List.fold(fun nlist node 
                           -> let adjlist = 
                                List.fold(fun adj edge 
                                            -> if edge.Node1.Name = node.Name or edge.Node2.Name = node.Name
                                                then edge::adj
                                                else adj) [] edges
                              node.Adjacent = Some(adjlist) |> ignore 
                              node::nlist
                              ) [] nodes
    match g with 
    | Dictionary_Graph(_,_) -> let dict = new Dictionary<string,Node<'a>>()
                               List.iter(fun node -> dict.Add(node.Name,node)) nlist |> ignore
                               Dictionary_Graph(dict,edges)
    | ConcurrentDictionary_Graph(_,_) -> let dict = new ConcurrentDictionary<string,Node<'a>>()

                                         List.iter(fun node -> dict.AddOrUpdate(node.Name,node,(fun k v -> node)) |> ignore) nlist |> ignore
                                         ConcurrentDictionary_Graph(dict,edges)

这是解决问题的正确方法吗?如果它不正确,有没有更快的方法来做我想在这里做的事情?

1 个答案:

答案 0 :(得分:3)

在F#中重复创建新的不可变列表绝对没有错,只要它是通过预先添加来完成的 - 就像你在这里一样。此操作为O(1),因此不应该是性能问题。事实上,列表可能是此类事物的最佳F#集合。

转到那里的代码,有一些问题。首先:

node.Adjacent = Some(adjlist) |> ignore 

此行检查node.Adjacent是否等于Some(adjlist),然后忽略该比较的结果。我怀疑你有意:

node.Adjacent <- Some(adjlist)

在折叠中使用可变性可能是一种奇怪的做法,我不会特别推荐。我建议您选择添加新的邻接列表来创建新节点。

示例:

// create new record with adjacency list supplied
{Item = node.Item; Name = node.Name; Adjacent = Some adjlist}  

其次,你的第二个List.fold看起来像是对我的过滤器。你可以替换:

List.fold (fun adj edge -> 
    if edge.Node1.Name = node.Name or edge.Node2.Name = node.Name then edge::adj
    else adj) [] edges

edges 
|> List.filter (fun edge -> edge.Node1.Name = node.Name || edge.Node2.Name = node.Name)

这不是问题,代码的一部分应该可以使用,但使用过滤器更简单,更简洁。

同样,您的第一个List.fold可以替换为List.map

最终结果可能是:

let buildGraph (nodes:Node<'a> list) (edges:Edge<'a> list) (g:Graph<'a>) = 
    let nlist = 
        nodes |> List.map (fun node ->
             let adjlist = 
                 edges |> List.filter(fun edge -> edge.Node1.Name = node.Name || edge.Node2.Name = node.Name)
             {Item = node.Item; Name = node.Name; Adjacent = Some adjlist})
    match g with 
    | DictionaryGraph(_) -> 
        let dict = new Dictionary<string,Node<'a>>()
        List.iter(fun node -> dict.Add(node.Name,node)) nlist |> ignore
        Dictionary_Graph(dict,edges)
    | ConcurrentDictionaryGraph(_) -> 
        let dict = new ConcurrentDictionary<string,Node<'a>>()
        List.iter(fun node -> dict.AddOrUpdate(node.Name,node,(fun k v -> node)) |> ignore) nlist |> ignore
        ConcurrentDictionaryGraph(dict,edges)