2012-01-16 14 views
6

Estoy trabajando en un código C# que trata problemas como promedios móviles, donde a menudo necesito tomar un List/IEnumerable y trabajar en trozos de datos consecutivos. El módulo F # Seq tiene una gran función, windowed, que al tomar una Sequence, devuelve una secuencia de fragmentos de elementos consecutivos.¿Hay un equivalente al F # Seq.windowed en C#?

¿Tiene C# una función equivalente lista para usar con LINQ?

+0

El usuario que proporcionó la respuesta aceptada admitió que era incorrecta, es posible que desee considerar la elección de otro ahora. – Kev

Respuesta

5

Siempre puede llamar a SeqModule.Windowed desde C#, solo necesita hacer referencia a FSharp.Core.Dll. Los nombres de las funciones también son un poco destrozado, por lo que llaman Windowed en lugar de windowed, de forma que encaje con las convenciones de C# capitalización

+0

¡Aquí, aquí, haciendo eso todo el tiempo! Seq.singleton, FSharpSet, lo que sea. Cuando tengo que codificar en C#, suelo utilizar el F # stdlib. ¡Cómo podría vivir sin eso! – kkm

+1

Es 'SeqModule.Windowed' en realidad. –

2

Siempre se puede liar (o traducir el de F # núcleo):

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) } 

Mi intento se ve así, es mucho más lento que simplemente llamar a F # directamente (como lo sugirió John Palmer). Supongo que es debido a F # utilizando una matriz sin comprobar .:

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; 
} 
+0

Reemplace la llamada a 'ArrayInit' con' var arrR = new T [windowSize]; for (int j = 0; j Daniel

+1

'Seq.windowed' utiliza' zeroCreateUnchecked', pero simplemente omite la validación del parámetro 'size' (es decir,' if size <0 then invalidArg ... '). No evita la comprobación de límites. Eso se hace a discreción del JITer, creo. – Daniel

+0

@Daniel, es agradable ver que alguien mordió el anzuelo :) Sin embargo, no puedo obtener tus resultados. Si hago 'var list = Enumerable.Range (0, 100000); var sw = Stopwatch.StartNew(); int count = list.Windowed (15) .Count(); sw.Stop(); 'y luego lo mismo con' Microsoft.FSharp.Collections.SeqModule.Windowed' (en un nuevo rango), el C# siempre lleva aproximadamente el doble de tiempo ... – Benjol

1

El Reactive Extensions tienen un número reducido de operadores para ayudar con esto, tal como Buffer y Window. Las extensiones interactivas, que se pueden encontrar en la rama experimental, agregan estos y un número significativo de operadores adicionales a LINQ.

Cuestiones relacionadas