簡體   English   中英

是否可以在f#中編寫這樣的遞歸分組函數

[英]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

我不認為你可以把它寫成一個遞歸函數,你有自己的約束 - 即:

  1. 表示分組的元組'key * seq<'value>
  2. 異構密鑰函數(或其集合) - 這是我通過“通過某個其他密鑰對每個子序列進行分組”所理解的。

如果您將分組表示為實際樹類型(而不是從元組構建的特殊樹),您可以有一些余地 - 這樣您就可以使用定義良好的遞歸結果類型來處理遞歸函數。

如果此時您還能夠在密鑰函數上進行折衷以使其成為同類(最壞情況 - 生成哈希碼),您應該能夠在類型系統中表達您想要的內容。

您當然可以使用非遞歸分組函數,該函數采用分組序列並在其上放置另一級別的分組 - 如下所示:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM