Recursion 在F中对平坦序列进行延迟分组#

Recursion 在F中对平坦序列进行延迟分组#,recursion,f#,immutability,Recursion,F#,Immutability,给出一系列项目,如下所示: [ ("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3) ] 我如何将其惰性地转换为: { ("a", { 1; 2; 3}); ("b", { 1 }); ("c", { 2; 3}) } 您可以假设输入数据源已按分组键元素排序,例如“a”、“b”和“c” 我使用{}表示它是一个延迟计算的项目序列 我已经让它必须在源序列的IEnumerator上运行两个while循环,但这涉及到创建引用变量和变异等

给出一系列项目,如下所示:

[ ("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3) ]
我如何将其惰性地转换为:

{ ("a", { 1; 2; 3}); ("b", { 1 }); ("c", { 2; 3}) }
您可以假设输入数据源已按分组键元素排序,例如“a”、“b”和“c”

我使用{}表示它是一个延迟计算的项目序列

我已经让它必须在源序列的IEnumerator上运行两个while循环,但这涉及到创建引用变量和变异等。我确信有更好的方法来做到这一点,可能是递归或使用Seq库中的一些操作,例如扫描或展开

Seq.groupBy fst

如果您想在IEnumerable上实现此功能,将执行此技巧。用于迭代输入的类型是必需的。但其余的可以使用序列表达式编写为递归函数

下面的代码在第一级是惰性的(它惰性地生成每个组),但它不会惰性地生成组的元素(我认为这会有非常微妙的语义):

///根据
///钥匙选择器功能“f”产生的钥匙
设groupf(输入:seq)=seq{
使用en=input.GetEnumerator()
//迭代元素并保留当前组的键
//以及迄今为止属于该集团的所有元素
让rec循环键acc=seq{
如果en.MoveNext()那么
设nkey=f en.电流
如果nkey=key,则
//如果键匹配,则将其追加到组中
屈服!循环键(en.Current::acc)
其他的
//否则,制作到目前为止收集的组并开始一个新的组
收益表修订版acc
屈服!循环nkey[当前]
其他的
//在序列的末尾,生成最后一组
收益表修订版acc
}
//以第一个键和第一个值作为累加器开始
如果en.MoveNext()那么
屈服!循环(f en.Current)[en.Current]}
不幸的是,这个(非常有用的!)函数没有包含在标准F#库中,因此如果您想对相邻元素(而不是使用
Seq.groupBy
对列表中的任意元素)进行分组,您必须自己定义它…

中有一个通用函数可用于:

let p = [("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3)]
let l = p |> Seq.groupBy fst |> Seq.map(fun x -> fst x, snd x |> Seq.map snd) 
#r "FSharpPlus.dll"
open FSharpPlus

seq [ ("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3) ]
    |> chunkBy fst 
    |> map (fun (x,y) -> x, map snd y)
它与
seq
array
list
一起工作


seq
的实现与Tomas的
groupdAdjacent
几乎相同。

我前一段时间做了类似的事情,也做了一些变异:这会给你
(“a”,“a”,1),(“a”,2)
。更重要的是,我认为Isaac在问如何对相邻元素进行分组,与输入中具有相同键的任何元素不同…不完全相同,它将为您提供一个Seq。而且,groupby不必遍历整个集合来获取分组-我不想这样做,因为我知道一旦键更改,分组将结束。控件看起来是一个非常好的主意。。我不知道这一点使用
Seq.groupBy
忽略了这样一个事实,即“您可以假设输入数据源已经按照分组键元素进行了排序”,因此它无法惰性地生成组,这正是OP所要求的。。。因此,这产生了正确的结果,但不是懒惰(这是问题的重点)。感谢托马斯解释为什么这不是我想要的(但感谢OP的建议)
#r "FSharpPlus.dll"
open FSharpPlus

seq [ ("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3) ]
    |> chunkBy fst 
    |> map (fun (x,y) -> x, map snd y)