假设我们在SQL数据库中有一个供应商表,我们想要加载到F#:
+----+--------+--------+
| ID | Name | Parent |
+----+--------+--------+
| 1 | Nest | 2 |
| 2 | Google | NULL |
| 3 | Apple | NULL |
+----+--------+--------+
使用类型提供程序很容易将表格转换为F#,但是假设我们希望将数据转换为供应商序列,其中Vendor是这样的类型:
Vendor = {ID: int; Name: String; Parent: Vendor option}
怎么会这样做呢?问题在于,在创建供应商序列时,我们无法映射到特定供应商的每一行,因为我们还没有供应商序列。最好还假设应用程序允许循环(A可以将B作为父级,B可以将A作为父级),尽管在供应商的情况下确实没有意义。
您可以将供应商类型定义为:
Vendor = {ID: int; Name: String; ParentID: int option}
但这似乎不那么优雅,因为每次你想要引用父供应商时你都必须进行某种查找。有没有一个已知的解决方案?这似乎是一种经常发生的情况(特别是在处理图形或树木时)。
似乎解决方案可能涉及某种懒惰的评估,但我不清楚Lazy<> T>。可以在此处应用F#中的类型。
答案 0 :(得分:2)
它不是一个特别优雅的解决方案,但是对父级使用延迟评估的解决方案看起来像这样:你将有两种类型,一种匹配你的表的模式,一种是递归的:
type Flat = { ID: int; Name: string; ParentID : int option}
type Recursive = { ID: int; Name: string; Parent: Lazy<Recursive> option}
然后让我们设置一些看起来像你的桌子的东西:
let records =
[
{ ID = 1; Name = "Nest"; ParentID = Some 2 }
{ ID = 2; Name = "Google"; ParentID = None }
{ ID = 3; Name = "Apple"; ParentID = None }
{ ID = 4; Name = "Yin"; ParentID = Some 5 }
{ ID = 5; Name = "Yang"; ParentID = Some 4 }
]
|> List.map (fun x -> x.ID, x)
|> Map.ofList
let getRecord recID = records |> Map.find recID
你可以像这样把它放在一起:
let rec getRecordRecursive recID =
let record = getRecord recID
{
ID = record.ID
Name = record.Name
Parent =
record.ParentID
|> Option.map (fun pid ->
Lazy.Create <| fun () ->
getRecordRecursive pid)
}
因此,在某种意义上,您使用惰性类型来延迟递归的下一步,直到您需要它为止。否则getRecordRecursive 4
会给你一个堆栈溢出。
但是存在权衡 - 例如,你不再在这些记录上获得良好的行为平等。从长远来看,我不相信你最好不要使用Flat
条记录。