是否有相当于C#中的F#Seq.windowed?

我正在研究一些处理移动平均线等问题的C#代码,我经常需要使用List / IEnumerable并处理连续数据块。 F#Seq模块具有很好的function,窗口化,它接收序列,返回一系列连续元素的块。

C#是否具有与LINQ开箱即用的等效function?

你总是可以从C#调用SeqModule.Windowed ,你只需要引用FSharp.Core.Dll 。 函数名称也略有损坏,因此您调用Windowed而不是windowed ,以便它符合C#大写约定

您可以随时自行滚动(或从F#核心转换一个):

 let windowed windowSize (source: seq<_>) = checkNonNull "source" source if windowSize <= 0 then invalidArg "windowSize" (SR.GetString(SR.inputMustBeNonNegative)) seq { let arr = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked windowSize let r = ref (windowSize-1) let i = ref 0 use e = source.GetEnumerator() while e.MoveNext() do arr.[!i] <- e.Current i := (!i + 1) % windowSize if !r = 0 then yield Array.init windowSize (fun j -> arr.[(!i+j) % windowSize]) else r := (!r - 1) } 

我的尝试看起来像这样,它比直接调用F#慢(如John Palmer建议的那样)。 我猜这是因为F#使用未经检查的数组:

 public static IEnumerable Windowed(this IEnumerable list, int windowSize) { //Checks elided var arr = new T[windowSize]; int r = windowSize - 1, i = 0; using(var e = list.GetEnumerator()) { while(e.MoveNext()) { arr[i] = e.Current; i = (i + 1) % windowSize; if(r == 0) yield return ArrayInit(windowSize, j => arr[(i + j) % windowSize]); else r = r - 1; } } } public static T[] ArrayInit(int size, Func func) { var output = new T[size]; for(var i = 0; i < size; i++) output[i] = func(i); return output; } 

Reactive Extensions有一些操作员可以帮助解决这个问题,比如Buffer和Window 。 可以在实验分支中找到的Interactive Extensions将这些以及大量额外的运算符添加到LINQ中。

约翰帕尔默的答案很棒,这是一个基于他答案的例子。

 var numbers = new[] {1, 2, 3, 4, 5}; var windowed = SeqModule.Windowed(2, numbers); 

您可能(或不想)将ToArray()添加到最后,如果没有ToArray,返回类型仍然在F#world(Sequence)中。 使用ToArray,它又回到了C#world(Array)。

在此处输入图像描述