简体   繁体   English

F#:尝试通过界面复制和更新记录时出错

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

I am trying to make a function that turns any flat seq<IHierarchy> into a hierarchy.我正在尝试制作一个将任何平面seq<IHierarchy>转换为层次结构的函数。 Essentially, anything that has a parentID and a seq of children should be able to be made into a hierarchy.从本质上讲,任何具有 parentID 和 seq 子级的东西都应该能够形成层次结构。 Instead of making Hierarchy a base class with parentID and children properties [we can't as records are sealed classes] I was wondering if it was possible to make it an IHierarchy with two abstract fields that we implement for each class (parentID and children).不是将 Hierarchy 设为具有 parentID 和 children 属性的基类 [我们不能因为记录是密封类] 我想知道是否有可能将它设为具有我们为每个类(父 ID 和孩子)实现的两个抽象字段的 IHierarchy .

I attached the code below including a makeHierarchy function that tries to turn a flat seq<IHierarchy> into a hierarchy structure of IHierarchies.我附上了下面的代码,其中包括一个 makeHierarchy 函数,该函数试图将平面seq<IHierarchy>转换为 IHierarchies 的层次结构。 However, when I try using the record copy and update syntax (ie: {node with children = ...}) I am getting an error saying "The type IHierarchy does not contain a field children".但是,当我尝试使用记录复制和更新语法(即:{node with children = ...})时,我收到一条错误消息,指出“IHierarchy 类型不包含字段 children”。 I am a bit confused how to get the record {with} syntax to work for this type in the interface.我有点困惑如何让记录 {with} 语法在界面中适用于这种类型。 Is it not possible to?不可能吗? Any help would be appreciated as I'm pretty new to F#.任何帮助将不胜感激,因为我对 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) )

Do you need an interface for this?你需要一个接口吗? You already have the source type defined by the JSON type provider.您已经拥有 JSON 类型提供程序定义的源类型。 Why not define a concrete destination type?为什么不定义一个具体的目的地类型?

In functional programming, the best designs usually separate data from behaviour.在函数式编程中,最好的设计通常将数据与行为分开。 Data is data, and functions implement the behaviour.数据就是数据,函数实现了行为。 You typically don't need polymorphic objects, although coming from an OOD background, it can be a hard habit to break.您通常不需要多态对象,尽管来自 OOD 背景,但它可能是一个很难改掉的习惯。

If you need a hierarchy, you can often model it using a generic record type like this:如果您需要一个层次结构,您通常可以使用这样的通用记录类型对其进行建模:

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

Assuming that you've already defined the SalesComponentJson type using the JSON type provider as above, you can define a function that transforms such JSON data to hierarchies:假设您已经使用上述 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 })

From the perspective of the type system, there's no guarantee that any given list of JSON data doesn't hold more than a single entry with no parent ID.从类型系统的角度来看,不能保证任何给定的 JSON 数据列表不包含多个没有父 ID 的条目。 Thus, the function returns a list of graphs, or, rather, a forest.因此,该函数返回一个图形列表,或者更确切地说,一个森林。

Here's some example data:以下是一些示例数据:

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" }""" ]

and here's a usage example from FSI:这是 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 = [];}];}];}]

This forest only has a single tree.这片森林只有一棵树。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM