簡體   English   中英

正確使用F#中的折疊

[英]Proper use of fold in F#

我是F#的新手。 我正在嘗試使用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      

您繼續使用相同的參數( fullListlist調用structureCategories 由於參數相同,因此它將繼續執行與上一遍完全相同的操作,並最終以相同的參數再次調用自身。 等等。

這是無界遞歸(“ unbounded”在這里表示“不知道何時停止遞歸”),它也不是“尾遞歸”,因此很自然地,它會導致堆棧溢出。

如果要將平面列表轉換為樹形結構,可以執行以下操作:

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) }

這樣,您將面臨兩個問題,在不了解您的要求的情況下,我不知道該如何解決:

  1. 如果原始列表碰巧有循環,這仍然會導致堆棧溢出。
  2. 您需要確定誰是樹的根。 直觀上,這可以通過“ empty” ParentId來表示,但是從您的數據結構尚不清楚“ empty”是什么意思。

最后,這種幼稚的解決方案雖然比原始解決方案要好,但仍然比需要的要慢一些。 它遍歷整個列表一次,並且對每個節點進行另一遍以確定其子級,從而導致O(N ^ 2)的整體復雜性。 如果您希望列表相對較小,則可能會很好,但對於較大列表,則不太好。 在這種情況下,我將首先將列表轉換為哈希表(由ParentId鍵入鍵),然后使用該表而不是List.filter查找子List.filter

多虧費奧多爾(Fyodor),我看到了我的錯誤。 他對提出同樣的論點一無所知。 我在foundSubCategory值之前添加了以下代碼:

let modifiedList = elem' :: List.empty<CategoryStructure>

然后在后續代碼中調用該值:

let foundSubCategory = 
        {
            Category = elem';
            SubCategories = structureCategories fullList modifiedList |> Seq.ofList
        }

這解決了我的問題,但是正如Fyodor提到的那樣,我現在必須將其重構為更有效的方法。

更新

根據Fyodor指出的見解,這是我代碼的當前狀態,它將替換原始代碼:

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    
        }

    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM