簡體   English   中英

F#:嘗試通過界面復制和更新記錄時出錯

[英]F#: Error when trying to copy and update record through interface

我正在嘗試制作一個將任何平面seq<IHierarchy>轉換為層次結構的函數。 從本質上講,任何具有 parentID 和 seq 子級的東西都應該能夠形成層次結構。 不是將 Hierarchy 設為具有 parentID 和 children 屬性的基類 [我們不能因為記錄是密封類] 我想知道是否有可能將它設為具有我們為每個類(父 ID 和孩子)實現的兩個抽象字段的 IHierarchy .

我附上了下面的代碼,其中包括一個 makeHierarchy 函數,該函數試圖將平面seq<IHierarchy>轉換為 IHierarchies 的層次結構。 但是,當我嘗試使用記錄復制和更新語法(即:{node with children = ...})時,我收到一條錯誤消息,指出“IHierarchy 類型不包含字段 children”。 我有點困惑如何讓記錄 {with} 語法在界面中適用於這種類型。 不可能嗎? 任何幫助將不勝感激,因為我對 F# 還很陌生。

module Hierarchy = 
    type IHierarchy =
        abstract member parentID: Option<int> 
        abstract member children: seq<IHierarchy>

module SalesComponents = 
    open Hierarchy
    type SalesComponentJson = JsonProvider<""" [{ "ID":1, "parentID":0, "name":"All Media" }, { "ID":1, "parentID":null, "name":"All Media" }]  """, SampleIsList=true>
    type SalesComponent  = {
                            ID: int; 
                            parentID: Option<int>; 
                            children: seq<SalesComponent>; 
                            name: string
                           }
                           interface IHierarchy with
                            member x.parentID = x.parentID 
                            member x.children = x.children |> Seq.map (fun c -> c :> IHierarchy)

open Hierarchy
open SalesComponents
let main argv =   
    let makeHierarchy hierarchyRecords:seq<IHierarchy> = 
        let root = hierarchyRecords |> Seq.tryFind (fun sc -> sc.parentID.IsNone)
        let rec getHierarchy (node: IHierarchy, scs: seq<IHierarchy>) = 
            {node with children = scs |> Seq.filter (fun sc -> sc.parentID.IsSome && sc.parentID.Value = node.ID )
                                      |> Seq.map    (fun sc -> getHierarchy(sc,scs))}
        root |> Option.map (fun r -> getHierarchy(r,hierarchyRecords) )

你需要一個接口嗎? 您已經擁有 JSON 類型提供程序定義的源類型。 為什么不定義一個具體的目的地類型?

在函數式編程中,最好的設計通常將數據與行為分開。 數據就是數據,函數實現了行為。 您通常不需要多態對象,盡管來自 OOD 背景,但它可能是一個很難改掉的習慣。

如果您需要一個層次結構,您通常可以使用這樣的通用記錄類型對其進行建模:

type Graph<'a> = { Node : 'a; Children : Graph<'a> list }

假設您已經使用上述 JSON 類型提供程序定義了SalesComponentJson類型,您可以定義一個將此類 JSON 數據轉換為層次結構的函數:

// FSharp.Data.JsonProvider<...>.Root list -> Graph<string> list
let createHierarchies (xs : SalesComponentJson.Root list) =
    let rec findChildren parentId =
        xs
        |> List.filter (fun x -> x.ParentId = Some parentId)
        |> List.map (fun x -> { Node = x.Name; Children = findChildren x.Id })

    xs
    |> List.filter (fun x -> x.ParentId.IsNone)
    |> List.map (fun root -> { Node = root.Name; Children = findChildren root.Id })

從類型系統的角度來看,不能保證任何給定的 JSON 數據列表不包含多個沒有父 ID 的條目。 因此,該函數返回一個圖形列表,或者更確切地說,一個森林。

以下是一些示例數據:

let salesComponents = [
    SalesComponentJson.Parse """{ "ID":0, "name":"All Media" }"""
    SalesComponentJson.Parse """{ "ID":1, "parentID":0, "name":"Foo" }"""
    SalesComponentJson.Parse """{ "ID":2, "parentID":1, "name":"Bar" }"""
    SalesComponentJson.Parse """{ "ID":3, "parentID":1, "name":"Baz" }"""
    SalesComponentJson.Parse """{ "ID":4, "parentID":0, "name":"Qux" }"""
    SalesComponentJson.Parse """{ "ID":5, "parentID":4, "name":"Corge" }""" ]

這是 FSI 的一個使用示例:

> createHierarchies salesComponents;;
val it : Graph<string> list =
  [{Node = "All Media";
    Children =
     [{Node = "Foo";
       Children = [{Node = "Bar";
                    Children = [];}; {Node = "Baz";
                                      Children = [];}];};
      {Node = "Qux";
       Children = [{Node = "Corge";
                    Children = [];}];}];}]

這片森林只有一棵樹。

暫無
暫無

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

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