[英]Is it possible to write a recursive grouping function like this in f#
假設您需要將序列分組為一系列元組。 每個元組都是一個關鍵* seq。 所以從某種意義上說,結果是一系列序列。
到目前為止一切都很標准。
如果您想通過其他鍵進一步對每個子序列進行分組,該怎么辦? 將另一個groupby函數映射到序列序列的每個元素上會很容易。 然后,您將擁有一系列序列序列。
開始變得有點毛茸茸。
如果你想進一步分組怎么辦?
是否可以編寫一個可以接受密鑰生成函數和任意序列的函數,並遞歸地展開圖層,然后使用keyFunction添加另一層分組?
我懷疑答案是否定的,因為遞歸函數沒有明確定義的類型。
我對此的嘗試,進一步說明了這個想法:
let rec recursiveGrouper keyFunction aSeq =
let first = Seq.head aSeq
match first with
| ((a:'a), _) -> Seq.map (fun (b,(c:seq<'c>)) -> (b, recursiveGrouper keyFunction c)) aSeq
| _ -> Seq.groupBy keyFunction aSeq
編輯:
讓我們添加一個如何工作的例子,它是可能的
type FruitRecord = {Fruit:string; Number:int; SourceFarm:string; Grade:float}
let key1 fr =
fr.Fruit
let key2 fr =
fr.SourceFarm
let key3 fr =
match fr.Grade with
|f when f > 5.0 -> "Very Good"
|f when f > 2.5 -> "Not bad"
|_ -> "Garbage"
假設我們在序列中有一大堆水果記錄。 我們想按照水果的類型對它們進行分組。
一種方法是說
let group1 = fruitRecs |> Seq.groupBy key1
使用我們的遞歸函數,這將是
let group1 = recursiveGrouper key1 fruitRecs
接下來,假設我們要按源服務器場對group1組中的每個項目進行分組。
我們可以說
let group2 =
group1
|> Seq.map (fun (f, s) -> (f, Seq.groupBy key2 s))
使用我們的遞歸函數就可以了
let group2 = recursiveGrouper key2 group1
我們可以進一步按照年級分組說
let group3 = recursiveGrouper key3 group2
我不認為你可以把它寫成一個遞歸函數,你有自己的約束 - 即:
'key * seq<'value>
, 如果您將分組表示為實際樹類型(而不是從元組構建的特殊樹),您可以有一些余地 - 這樣您就可以使用定義良好的遞歸結果類型來處理遞歸函數。
如果此時您還能夠在密鑰函數上進行折衷以使其成為同類(最壞情況 - 生成哈希碼),您應該能夠在類型系統中表達您想要的內容。
您當然可以使用非遞歸分組函數,該函數采用分組序列並在其上放置另一級別的分組 - 如下所示:
module Seq =
let andGroupBy (projection: 't -> 'newKey) (source: seq<'oldKey * seq<'t>>) =
seq {
for key, sub in source do
let grouped = Seq.groupBy projection sub
for nkey, sub in grouped do
yield (key, nkey), sub
}
使用FruitRecord示例:
values
|> Seq.groupBy key1
|> Seq.andGroupBy key2
|> Seq.andGroupBy key3
實際上有一些方法可以使用靜態約束使遞歸函數工作。 這是一個小例子:
// If using F# lower than 4.0, use this definition of groupBy
module List =
let groupBy a b = Seq.groupBy a (List.toSeq b) |> Seq.map (fun (a, b) -> a, Seq.toList b) |> Seq.toList
type A = class end // Dummy type
type B = class end // Dummy type
type C =
inherit B
static member ($) (_:C, _:A ) = fun keyFunction -> () // Dummy overload
static member ($) (_:C, _:B ) = fun keyFunction -> () // Dummy overload
static member ($) (_:B, aSeq) = fun keyFunction -> List.groupBy keyFunction aSeq // Ground case overload
static member inline ($) (_:C, aSeq) = fun keyFunction -> List.map (fun (b, c) -> b, (Unchecked.defaultof<C> $ c) keyFunction) aSeq
let inline recursiveGrouper keyFunction aSeq = (Unchecked.defaultof<C> $ aSeq) keyFunction
// Test code
type FruitRecord = {Fruit:string; Number:int; SourceFarm:string; Grade:float}
let key1 fr = fr.Fruit
let key2 fr = fr.SourceFarm
let key3 fr =
match fr.Grade with
|f when f > 5.0 -> "Very Good"
|f when f > 2.5 -> "Not bad"
|_ -> "Garbage"
let fruitRecs = [
{Fruit = "apple" ; Number = 8; SourceFarm = "F"; Grade = 5.5}
{Fruit = "apple" ; Number = 5; SourceFarm = "F"; Grade = 4.5}
{Fruit = "orange"; Number = 8; SourceFarm = "F"; Grade = 5.5}
]
let group1 = recursiveGrouper key1 fruitRecs
let group2 = recursiveGrouper key2 group1
let group3 = recursiveGrouper key3 group2
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.