F#:是否可以使用可变的 .Net List 将元素添加到同一列表

F# : Is it okay to use mutable .Net List to add elements to the same list

在一个循环中,我将一个新元素添加到我在下一次迭代中需要的列表中,为此我使用了可变的 .Net List<T>.

F# 鼓励一般使用不可变集合,似乎我无法使用不可变 listseq.

实现我想要的

继续使用可变的 .Net List<T> 是可以接受的,还是鼓励只使用不可变的?如果可以,我该如何实现?

我的代码有点长和复杂,所以让我们考虑一下这个伪 F# 代码:

let children = new List<node>()
let bestBranchEntropies = entropiesSuchAs data.Rows parents
//Finding the best children for the best parent
bestBranchEntropies |> Seq.iter (fun bestBranch -> let attribut = IGetTheAttributByMaximumGainHere
                                                   //Creating one of the children in every iteration
                                                   let node = {
                                                                 content = attribut;
                                                                 branch = Some(fst bestBranch);
                                                                 children = None;
                                                                 isLeaf = false;
                                                              }
                                                   //Considering it a child
                                                   children.Add node

                                )
                       //After having all the children
                       let children' = children |> Seq.map (fun child ->  {
                                                                            content = child.content; 
                                                                            branch = child.branch; 
                                                                            children = //recursive call here to do the same work above (getting children again, yeah it's a tree)
                                                                            isLeaf = child.isLeaf;
                                                                          })

                       Some(children')

据我所知,当然不需要可变列表(如果您的伪代码完全反映了问题)。考虑以下因素:

let children = 
    bestBranchEntropies 
    |> Seq.map (fun bestBranch -> 
        let attribut = IGetTheAttributByMaximumGainHere
        //Creating one of the children in every iteration
        {
            content = attribut;
            branch = Some(fst bestBranch);
            children = None;
            isLeaf = false;
        }
    |> Seq.toList
children 
|> Seq.map (fun child ->  
    {
        content = child.content
        branch = child.branch
        children = //recursive call here to do the same work above (getting children again, yeah it's a tree)
        isLeaf = child.isLeaf
    }
)
|> Some

第一个 Seq.toList 可以跳过,您可以一直使用管道。从你的伪代码看来,整个第二个循环实际上可以安全地与第一个循环合并?

从你的伪代码看不是很清楚,但我知道你问的真正问题是"can I use a mutable list instead of an accumulator when traversing a tree structure?"。

我觉得没问题,前提是:

  1. 可变对象是函数的本地对象,不能以其他方式访问和篡改,
  2. 它的目的用注释清楚地标明了,这样未来的维护者在重构代码时就不会被它绊倒(并且错误地假设可变对象是不可变的)。

它确实节省了为树编写适当的尾递归 fold 所需的大量时间(这当然也是一个选项,请参阅 this blog post series)。即使您最终写了一个 fold,首先对您的方法进行原型设计也是一个不错的选择。

作为旁注,我更喜欢使用 ref cells + 不可变集合而不是可变集合来达到这个目的。与在可变列表上调用 Add 相比,改变引用单元格所需的额外语法使它非常清楚发生了什么。