Existe um equivalente a F # Seq.windowed em C #?
Pergunta
Estou trabalhando em alguns códigos C # que lidam com problemas como médias móveis, onde frequentemente preciso pegar um List / IEnumerable e trabalhar em blocos de dados consecutivos.O módulo F # Seq tem uma ótima função, windowed, que pegando uma Sequence, retorna uma sequência de pedaços de elementos consecutivos.
O C # tem uma função equivalente pronta para uso com o LINQ?
Solução
Você pode sempre chamar SeqModule.Windowed
em C #, você só precisa fazer referência a FSharp.Core.Dll
.Os nomes das funções também estão ligeiramente distorcidos, então você chama Windowed
em vez de windowed
, para que se encaixe nas convenções de capitalização C #
Outras dicas
Você sempre pode fazer o seu próprio (ou traduzir o do núcleo do 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) }
Minha tentativa se parece com isso, é muito mais lenta do que apenas chamar F # diretamente (como sugerido por John Palmer).Suponho que seja por causa de F # usando uma matriz não verificada:
public static IEnumerable<T[]> Windowed<T>(this IEnumerable<T> 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<T>(windowSize, j => arr[(i + j) % windowSize]);
else
r = r - 1;
}
}
}
public static T[] ArrayInit<T>(int size, Func<int, T> func)
{
var output = new T[size];
for(var i = 0; i < size; i++) output[i] = func(i);
return output;
}
As Extensões reativas têm alguns operadores para ajudar com isso, como Buffer e Janela .As extensões interativas, que podem ser encontradas no ramo experimental, adicionam essas e um número significativo de operadores adicionais ao LINQ.
A resposta de John Palmer é ótima, aqui está um exemplo baseado em sua resposta.
var numbers = new[] {1, 2, 3, 4, 5};
var windowed = SeqModule.Windowed(2, numbers);
Você pode (ou não) querer adicionar ToArray () ao final, sem ToArray, o tipo de retorno ainda está no mundo F # (Sequence).Com ToArray, está de volta ao mundo C # (Array).