F #:

2016-03-21 27 views
4

seq<IHierarchy> no'lu diziyi bir hiyerarşiye dönüştüren bir işlev yapmaya çalışıyorum. Esasen, bir ebeveyn kimliği ve bir çocuk grubu olan her şey bir hiyerarşiye dönüştürülebilmelidir. Hiyerarşi yapmak için altyazı ve alt özellikleri olan bir taban sınıfı [kayıtlar mühürlü sınıflar olarak yapamıyoruz] yerine, her sınıf için uyguladığımız iki soyut alanla (ebeveynlik ve çocuklar) bir IHiyerarşi yapmanın mümkün olup olmadığını merak ediyordum. .F #:

Düz bir seq<IHierarchy> IHierarchies'in hiyerarşi yapısına dönüştürmeye çalışan makeHierarchy işlevi de dahil olmak üzere aşağıdaki kodu ekledim. Ancak, kayıt kopyasını kullanmayı ve sözdizimini güncellediğimde (örneğin: {node with children = ...}) "IHierarchy türü bir alan çocuğu içermiyor" diyerek bir hata alıyorum. Arayüzde bu tür için çalışmak için sözdizimi {with} kaydı nasıl elde edeceğimi biraz kafam karıştı. Bu mümkün değil mi? F # için oldukça yeni olduğum için herhangi bir yardım mutluluk duyacağız.

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)) 
+2

IHeirachy bir kayıt değildir, bu nedenle kayıt sözdizimini kullanamazsınız. –

+0

Ben de böyle düşündüm. Bu sorunu çözmek için F # tasarım deseninde herhangi bir öneriniz var mı? Tümüyle düşündüğüm seçenekler kötü görünüyor: 1. Bir kayıt türü kullanmayın, ebeveyn kimliği ve alt alanlar içeren bir temel sınıf kullanın ve bu temel sınıftan doğrudan doğruya 2. Bir klonlama işlevinin bir parçası olarak kullanın. Arayışım, çocukların alanlarını değiştirebilir hale getir, fonksiyonumda kayıt klonu ve sonra da çocuk alanını el ile değiştir. Her ikisi de IHierarchy arabiriminin bir parçası olduğundan, bu bir sorun olmamalı, ancak çirkin görünüyor. – gnicholas

+1

Zarif bir alternatif göremiyorum. –

cevap

3

Bunun için bir arabirime ihtiyacınız var mı? JSON tipi sağlayıcı tarafından tanımlanan kaynak türünüz zaten var. Neden bir beton hedef türü tanımlamıyorsunuz?

İşlevsel programlamada, en iyi tasarımlar genellikle verileri davranıştan ayırır. Veriler veridir ve işlevler davranışı uygular. Tipik olarak polimorfik objelere ihtiyacınız yoktur, ancak bir OOD arka planından gelmekle birlikte, kırılması zor bir alışkanlık olabilir.

bir hiyerarşi gerekiyorsa, genellikle bu gibi genel bir rekor türünü kullanarak bunu modelleyebilirsiniz:

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

Zaten yukarıdaki JSON tipi sağlayıcısını kullanarak SalesComponentJson türü tanımladığınız varsayarsak, yapabilirsiniz hiyerarşileri için böyle JSON veri dönüştüren bir işlevi tanımlayın: tip sistemleri açısından

// 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 veri herhangi bir listesi hiçbir üst kimliği ile tek bir girişinden daha barındırmama garantisi yoktur. Böylece, işlev bir grafik listesi veya daha ziyade bir ormanı döndürür.

İşte bazı örnek veriler var:

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

ve burada FSI bir kullanım örnek:

> 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 = [];}];}];}] 

Bu orman sadece tek ağacı vardır.

+0

Çok yardımcı yanıt. Anlamadığım en önemli şey, bu gibi kayıtlarla jeneriklerin kullanılmasıydı. Tamamen bunu deneyecek. – gnicholas