F# F中fold的正确使用#

F# F中fold的正确使用#,f#,F#,我是新来的。我正在尝试使用List.fold帮助我根据类别和子类别的Id和ParentId字段生成类别和子类别的列表。看起来我可能使这段代码比需要的更复杂,因为我得到了stackoverflow错误。我做错了什么或错过了什么?感谢所有相关反馈 // types type CategoryStructure = { Id: ValidString; ParentId: ValidString; Name: ValidString; Abbreviation: Val

我是新来的。我正在尝试使用List.fold帮助我根据类别和子类别的Id和ParentId字段生成类别和子类别的列表。看起来我可能使这段代码比需要的更复杂,因为我得到了stackoverflow错误。我做错了什么或错过了什么?感谢所有相关反馈

// types
type CategoryStructure = {
    Id: ValidString;
    ParentId: ValidString;
    Name: ValidString;
    Abbreviation: ValidString;
    Description: ValidString;
    SapId: ValidString;
    Section: ValidString;
    SectionPosition: ValidString
}

type DynamicCategories = {
    Category: CategoryStructure;
    SubCategories: seq<DynamicCategories>
}

// this is the function that produces the stack overflow error
let rec private structureCategories (fullList: CategoryStructure list) 
    (list: CategoryStructure list)  =

    List.fold (fun acc elem -> 


                    // get all categories and details
                    let categories = fullList
                    let mainAcc =
                        [

                            for row in categories do
                                if row = elem
                                then
                                    let subs =  
                                        List.fold (fun acc' elem' ->

                                                    if row.Id = elem'.ParentStructureId
                                                    then

                                                        let foundSubCategory = 
                                                            {
                                                                Category = elem';
                                                                SubCategories = structureCategories fullList list |> Seq.ofList
                                                            }
                                                        foundSubCategory :: acc'
                                                    else acc'

                                                    ) List.empty<DynamicCategories> categories
                                        |> Seq.ofList        
                                    yield{
                                        Category = elem;
                                        SubCategories = subs
                                    }


                        ]

                    mainAcc @ acc
                    ) List.empty<DynamicCategories> list  

// this function gets the initial parent categories and calls the above function
let getStructuredCategories () =

        let categories = allCategoriesAndDetails () |> List.ofSeq
        [
            for row in categories do
                if row.ParentStructureId = NotValid
                then yield row
        ] |> structureCategories categories |> Seq.ofList      
//类型
类型类别结构={
Id:有效字符串;
ParentId:ValidString;
名称:ValidString;
缩写:ValidString;
描述:有效字符串;
SapId:有效;
第节:有效期;
分区位置:有效字符串
}
类型DynamicCategories={
类别:类别结构;
子类别:seq
}
//这是产生堆栈溢出错误的函数
let rec private structureCategories(完整列表:类别结构列表)
(列表:类别结构列表)=
List.fold(趣味acc元素->
//获取所有类别和详细信息
让类别=完整列表
让我来=
[
对于类别中的行,请执行以下操作:
如果行=元素
然后
设subs=
List.fold(趣味acc'elem'->
如果row.Id=elem.ParentStructureId
然后
设子类别=
{
类别=元素';
子类别=结构类别完整列表|>序列列表
}
foundSubCategory::acc'
否则acc'
)列表。空类别
|>顺序列表
屈服{
类别=要素;
子类别=子类别
}
]
mainAcc@acc
)列表。空列表
//此函数获取初始父类别并调用上述函数
让getStructuredCategories()=
让categories=allCategoriesAndDetails()|>List.ofSeq
[
对于类别中的行,请执行以下操作:
如果row.ParentStructureId=无效
然后让行
]|>结构类别类别|>列表顺序

您继续使用相同的参数调用
结构类别
完整列表
列表
。因为参数是相同的,所以它继续执行与上一个过程完全相同的操作,并使用相同的参数再次调用自己。等等

这是无界递归(“无界”在这里的意思是“不知道什么时候停止循环”),也不是“尾部递归”,所以很自然,它会导致堆栈溢出

如果要将平面列表转换为树状结构,可以执行比此简单一点的操作:

let getChildren fullList parentId = fullList |> List.filter (fun c -> c.ParentId = parentId)
let rec toTree fullList root =
  { Category = root;
    SubCategories = 
      getChildren fullList root.Id 
      |> List.map (toTree fullList) }
这样,您将面临两个问题,如果不进一步了解您的需求,我不知道如何解决这两个问题:

  • 如果原始列表恰好有循环,这仍然会导致堆栈溢出
  • 您需要决定谁是树的根。直观地说,这将通过“empty”
    ParentId
    来表示,但从您的数据结构来看,“empty”的含义并不清楚

  • 最后,这个天真的解决方案虽然比您原来的解决方案好,但仍然比需要的慢一些。它在整个列表上迭代一次,对每个节点执行另一次传递以确定其子节点,从而导致总体复杂性为O(N^2)。如果您期望相对较小的列表,这可能很好,但对于较大的列表则不太好。在这种情况下,我会首先将列表转换为哈希表(由
    ParentId
    键入),然后使用该哈希表来查找子项,而不是
    list.filter

    使用相同的参数继续调用
    structureCegories
    fullList
    list
    。因为参数是相同的,所以它继续执行与上一个过程完全相同的操作,并使用相同的参数再次调用自己。等等

    这是无界递归(“无界”在这里的意思是“不知道什么时候停止循环”),也不是“尾部递归”,所以很自然,它会导致堆栈溢出

    如果要将平面列表转换为树状结构,可以执行比此简单一点的操作:

    let getChildren fullList parentId = fullList |> List.filter (fun c -> c.ParentId = parentId)
    let rec toTree fullList root =
      { Category = root;
        SubCategories = 
          getChildren fullList root.Id 
          |> List.map (toTree fullList) }
    
    这样,您将面临两个问题,如果不进一步了解您的需求,我不知道如何解决这两个问题:

  • 如果原始列表恰好有循环,这仍然会导致堆栈溢出
  • 您需要决定谁是树的根。直观地说,这将通过“empty”
    ParentId
    来表示,但从您的数据结构来看,“empty”的含义并不清楚

  • 最后,这个天真的解决方案虽然比您原来的解决方案好,但仍然比需要的慢一些。它在整个列表上迭代一次,对每个节点执行另一次传递以确定其子节点,从而导致总体复杂性为O(N^2)。如果您期望相对较小的列表,这可能很好,但对于较大的列表则不太好。在这种情况下,我会首先将列表转换为一个哈希表(由
    ParentId
    键入),然后使用该哈希表来查找子项,而不是
    列表。过滤器

    多亏了Fyodor,我发现了我的错误。他对提出同样的论点一窍不通。我在之前添加了这段代码
    let foundSubCategory = 
            {
                Category = elem';
                SubCategories = structureCategories fullList modifiedList |> Seq.ofList
            }
    
    let getStructuredCategories ()  =
    
        let fullList = allCategoriesAndDetails () 
    
        let parentList () =
            allCategoriesAndDetails ()
            |> Seq.filter (fun p -> p.ParentStructureId = NotValid)
    
        let rec toTree (fullList': seq<CategoryStructure>) (parent: CategoryStructure) =
    
            fullList'
            |> Seq.filter (fun x -> x.ParentStructureId = parent.Id)
            |> Seq.map (fun x -> 
                            {
                                Category = x;
                                SubCategories =
                                    toTree fullList' x
                            })
        seq {
            for row in parentList () do
            yield {
    
                Category = row;
                SubCategories = toTree fullList row    
            }
    
        }