Function 可以在f中编写这样的递归分组函数吗#
假设您需要将序列分组为元组序列。每个元组都是一个键*seq。所以在某种意义上,结果是一系列的序列 到目前为止,一切都相当标准 如果您想通过其他键对每个子序列进行进一步分组,该怎么办?将另一个groupby函数映射到序列序列的每个元素上是很容易的。然后你会有一系列的序列 开始有点毛茸茸的 如果你想把它进一步分组呢 是否可以编写一个函数,该函数可以接受一个键生成函数和任意序列,然后递归地展开层,然后使用键函数添加另一层分组 我怀疑答案是否定的,因为递归函数没有定义良好的类型 我的尝试是为了进一步说明这一观点:Function 可以在f中编写这样的递归分组函数吗#,function,recursion,types,f#,Function,Recursion,Types,F#,假设您需要将序列分组为元组序列。每个元组都是一个键*seq。所以在某种意义上,结果是一系列的序列 到目前为止,一切都相当标准 如果您想通过其他键对每个子序列进行进一步分组,该怎么办?将另一个groupby函数映射到序列序列的每个元素上是很容易的。然后你会有一系列的序列 开始有点毛茸茸的 如果你想把它进一步分组呢 是否可以编写一个函数,该函数可以接受一个键生成函数和任意序列,然后递归地展开层,然后使用键函数添加另一层分组 我怀疑答案是否定的,因为递归函数没有定义良好的类型 我的尝试是为了进一步说明
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
假设我们有一系列的水果记录。我们想按水果的种类对它们进行分类
一种方法是说
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实际上有一些方法可以使用静态约束使递归函数工作。下面是一个小例子:
// 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
//如果使用F#低于4.0,请使用此groupBy定义
模块列表=
让groupBy a b=Seq.groupBy a(List.toSeq b)|>Seq.map(fun(a,b)->a,Seq.toList b)|>Seq.toList
类型A=类结束//虚拟类型
类型B=类结束//虚拟类型
C型=
继承B
静态成员($)(:C,:A)=fun-keyFunction->()//虚拟重载
静态成员($)(:C,:B)=fun-keyFunction->()//虚拟重载
静态成员($)(:B,aSeq)=fun-keyFunction->List.groupBy-keyFunction-aSeq//Ground-case-重载
静态成员内联($)(:C,aSeq)=fun-keyFunction->List.map(fun(b,C)->b,(Unchecked.defaultof$C)keyFunction)aSeq
让内联递归Grouper键函数aSeq=(Unchecked.defaultof$aSeq)键函数
//测试代码
类型FruitRecord={Fruit:string;Number:int;SourceFarm:string;Grade:float}
设键1 fr=fr.果
设key2 fr=fr.SourceFarm
让键3 fr=
将fr等级与
|f当f>5.0时->非常好
|f当f>2.5->“不错”
|_->“垃圾”
let fructRecs=[
{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}
]
设group1=recursiveGrouper键1
设group2=recursiveGrouper键2 group1
设group3=recursiveGrouper键3 group2
你能从给定的输入中添加至少两个预期输出的示例吗?如果你真的不想recursiveGrouper
是递归的,那么请检查我的答案的编辑。我认为可能会用静态约束来破解某些东西。我可以试一试。@Gustavo:当然,那会很有趣的。那是我通常不会穿过的河流。但是,您应该检查对问题的编辑;)完成,看看河的另一边有什么;)@古斯塔沃:是的,另一边仍然很可怕。我不想维护这样的代码。我承认这是一个有趣的技术,你的工具箱里有。是的,这是一个很大的尺寸:)但我同意你的观点,特别是对于这个特定的函数。我喜欢使用这些技巧来达到一个非常高的通用性水平,但这里不是这样。这在我的机器上似乎不起作用。我认为List.groupBy需要更改为Seq.groupBy,即使这样,我在递归Grouper的第二个应用程序中也会出现一个错误:“水果记录”类型与“string*Seq”类型不匹配是的,我使用了List而不是Seq。如果你愿意,我可以把它改成seqs。但我发布的代码应该对您有用,不是吗?对我来说,列表中没有groupBy。但我使用的是f#3.1,也许f#4上的情况有所不同?@ChechyLevas是的,它附带f#4.0,但您可以添加它。看更新。你已经做了,所以我接受你的答案,尽管我不明白你是怎么做的!
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
}
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