给出一系列项目如下:
[ ("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库中的一些操作,例如扫描还是展开?
答案 0 :(得分:5)
如果你想在IEnumerable<'T>
上实现它(使它变得懒惰),那么它必然会有些必要,因为用于迭代输入的IEnumerator<'T>
类型是必要的。但其余部分可以使用序列表达式编写为递归函数。
以下是第一级中的懒惰(它会懒惰地生成每个组),但它不会懒惰地生成该组的元素(我认为这将具有非常微妙的语义):
/// Group adjacent elements of 'input' according to the
/// keys produced by the key selector function 'f'
let groupAdjacent f (input:seq<_>) = seq {
use en = input.GetEnumerator()
// Iterate over elements and keep the key of the current group
// together with all the elements belonging to the group so far
let rec loop key acc = seq {
if en.MoveNext() then
let nkey = f en.Current
if nkey = key then
// If the key matches, append to the group so far
yield! loop key (en.Current::acc)
else
// Otherwise, produce the group collected so far & start a new one
yield List.rev acc
yield! loop nkey [en.Current]
else
// At the end of the sequence, produce the last group
yield List.rev acc
}
// Start with the first key & first value as the accumulator
if en.MoveNext() then
yield! loop (f en.Current) [en.Current] }
不幸的是,这个(非常有用!)函数不包含在标准F#库中,所以如果你想使用Seq.groupBy
对相邻的元素(而不是列表中的任意元素)进行分组),你必须自己定义......
答案 1 :(得分:4)
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)
答案 2 :(得分:2)
#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
几乎相同。
答案 3 :(得分:1)
Seq.groupBy fst
会做的伎俩