@TheQuickBrownFox在建模領域做得很好。
type Employee = { Id : int; SupervisorId : int option; PersonalSales : double }
使用記錄/類來表示一個Tree
是處理事情, 這可能是更容易掌握,當你沒有很多經驗FP一個面向對象的方式。我想告訴你a more functional approach。
type 'a Tree =
| Leaf of 'a
| Branch of 'a * 'a Tree list
的Leaf
節點是SalesPerson
s的層次結構的端部。 Supervisor
和他們所有的爪牙都代表Branch
es並一路走高。
type SalesMember =
| SalesPerson of Employee
| Supervisor of Employee * SalesMember List
一個Tree
也將有一個根節點 - 只能有一個 - 你可以很容易地編寫一個函數變換rawData
喜歡的東西:
let rawData =
[ 0, None, 0.0
1, Some 0, 100.00
2, Some 0, 110.00
3, Some 1, 50.00
4, Some 1, 75.00
5, Some 2, 80.00
6, Some 0, 92.00 ]
let flatList =
rawData
|> List.map (fun (id, superId, sales) ->
{Id = id; SupervisorId = superId; PersonalSales = sales})
let getTree salesPeople =
// To do : validate root
let root = salesPeople |> List.find (fun p -> p.SupervisorId = None)
let children supervisorId =
salesPeople |> List.filter (fun p -> p.SupervisorId = Some supervisorId)
let rec loop employee =
match children employee.Id with
| [] -> SalesPerson employee
| list -> Supervisor (employee, List.map loop list)
loop root
let salesForce = getTree flatList
要實現GroupSales
你可以擴大Supervisor
。建設這棵樹的實例
type SalesMember =
| SalesPerson of emp : Employee
| Supervisor of emp : Employee * reports : List<SalesMember> * groupSales : double
一種方法是從getTree
功能轉化的樹。處理,轉化和優化樹木是一個廣泛的主題,因爲總是for fun and profit是開始你的旅程的好地方。
更新 - GroupSales
爲了簡單起見,我會只用一個識別聯合,在第一次運行設置GroupSales
爲零。但是,您可以輕鬆地將代碼轉換爲另一種類型的Tree
。
type Employee = { Id : int; SupervisorId : int option; PersonalSales : double }
type GroupSales = double
type SalesMember =
| SalesPerson of Employee
| Supervisor of Employee * SalesMember List * GroupSales
let rawData =
[ 0, None, 0.
1, Some 0, 100.00
2, Some 0, 110.00
3, Some 1, 50.00
4, Some 1, 75.00
5, Some 2, 80.00
6, Some 0, 92.00 ]
let flatList =
rawData
|> List.map (fun (id, superId, sales) ->
{Id = id; SupervisorId = superId; PersonalSales = sales})
let getTree salesPeople =
let root = salesPeople |> List.find (fun p -> p.SupervisorId = None)
let children supervisorId =
salesPeople |> List.filter (fun p -> p.SupervisorId = Some supervisorId)
let rec loop employee =
match children employee.Id with
| [] -> SalesPerson employee
| list -> Supervisor (employee, List.map loop list, 0.)
loop root
let transformTree root =
let rec getGroupSales = function
| SalesPerson emp
-> emp.PersonalSales
| Supervisor (sup, reports, _)
-> sup.PersonalSales + List.sumBy getGroupSales reports
let rec loop = function
| Supervisor (sup, reports, _) as mem
-> Supervisor (sup, List.map loop reports, getGroupSales mem)
| salesPerson -> salesPerson
loop root
let salesForce =
flatList
|> getTree
|> transformTree
一個不太懂事的實施將變換/鈣GroupSales
bottom-up instead of top-down,讓您的使用已經計算GroupSales
。
太棒了....我開始看到你是如何構建結構的。不過,我確實有第4點,我不知道該怎麼做。你會怎麼做'GroupSales'? (自己的「個人銷售」+兒童的「個人銷售」)。我只想計算一次,而不是每次我們要求它。 TIA,David –
@CrazySpaniard這是可能的,因爲我們在建立給定節點時有後代節點數據可用。但是,它的確意味着延遲創建記錄類型,直到後來我們可以計算'GroupSales'。這可能意味着更早的類型安全性(就像我上面所做的那樣,使用未標記的元組更長時間)或爲原始數據創建中間類型。 – TheQuickBrownFox
@CrazySpaniard我假設你的意思是'GroupSales' ='PersonalSales' +孩子的'GroupSales',它相當於'PersonalSales' + *後代'*'PersonalSales'。 – TheQuickBrownFox