我正在尝试创建一个将任何平面seq<IHierarchy>
转换为层次结构的函数。基本上,任何具有parentID和seq子元素的东西都应该能够构成一个层次结构。而不是使Hierarchy成为具有parentID和子属性的基类[我们不能将记录作为密封类]我想知道是否有可能使它成为一个IHierarchy,其中包含我们为每个类实现的两个抽象字段(parentID和儿童)。
我附上了下面的代码,包括一个makeHierarchy函数,它试图将一个平面seq<IHierarchy>
转换为IHierarchies的层次结构。但是,当我尝试使用记录复制和更新语法(即:{node with children = ...})时,我收到错误说&#34;类型IHierarchy不包含字段children&#34;。我有点困惑如何在接口中获取记录{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) )
答案 0 :(得分:3)
你需要一个接口吗?您已经拥有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 = [];}];}];}]
此林只有一棵树。