F#:尝试通过界面复制和更新记录时出错 [英] F#: Error when trying to copy and update record through interface

查看:111
本文介绍了F#:尝试通过界面复制和更新记录时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个将任何平面 seq< IHierarchy> 变成层次结构的函数。从本质上讲,任何具有parentID和seq的孩子都应该能够成为一个层次结构。而不是使Hierarchy成为具有parentID和children属性的基类[我们不能将记录作为密封类]我想知道是否有可能使它成为一个具有两个抽象字段的IHierarchy,我们为每个类实现了一个(parentID和children) 。

我附上了下面的代码,其中包括一个makeHierarchy函数,它试图将一个平面 seq< IHierarchy> 变成一个层次结构IHierarchies结构。但是,当我尝试使用记录副本和更新语法(即:{node with children = ...})时,出现错误,提示类型IHierarchy不包含字段子项。我有点困惑如何获得记录{with}语法在界面中为这种类型工作。这不可能吗?任何帮助将不胜感激,因为我对F#非常陌生。

  module Hierarchy = 
type IHierarchy =
抽象成员parentID:选项< int>
抽象成员子项:seq< IHierarchy>

模块SalesComponents =
开放层次结构
类型SalesComponentJson = JsonProvider<[{ID:1,parentID:0,name:All Media },{ID:1,parentID:null,name:All Media}],SampleIsList = true>
type SalesComponent = {
ID:int;
parentID:选项< int>;
children:seq< SalesComponent>;
name:string
}
带有
成员的接口IHierarchy x.parentID = x.parentID
成员x.children = x.children |> Seq.map(fun c - > c:> IHierarchy)

打开层次结构
打开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;儿童:图<'a>列表} 

假设您已经定义了 SalesComponentJson 使用上面提供的JSON类型提供者,你可以定义一个函数将这样的JSON数据转换为层次结构:

  / / FSharp.Data.JsonProvider< ...> .Root list  - >图表<串GT; list 
let createHierarchies(xs:SalesComponentJson.Root list)=
let rec findChildren parentId =
xs
|> List.filter(fun x - > x.ParentId =一些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。解析{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:图< string> list =
[{Node =All Media;
Children =
[{Node =Foo;
Children = [{Node =Bar;
Children = [];}; {Node =Baz;
Children = [];}];};
{Node =Qux;
Children = [{Node =Corge;
Children = [];}];}];}]

一棵树。


I am trying to make a function that turns any flat seq<IHierarchy> into a hierarchy. Essentially, anything that has a parentID and a seq of children should be able to be made into a hierarchy. 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).

I attached the code below including a makeHierarchy function that tries to turn a flat seq<IHierarchy> into a hierarchy structure of 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". I am a bit confused how to get the record {with} syntax to work for this type in the interface. Is it not possible to? Any help would be appreciated as I'm pretty new to 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. 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.

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:

// 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. 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:

> 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.

这篇关于F#:尝试通过界面复制和更新记录时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆