【问题标题】:Is there an equivalent to the F# Seq.windowed in C#?是否有与 C# 中的 F# Seq.windowed 等效的功能?
【发布时间】:2012-02-11 02:13:23
【问题描述】:

我正在编写一些 C# 代码来处理诸如移动平均线之类的问题,我经常需要使用 List / IEnumerable 并处理连续数据块。 F# Seq 模块有一个很棒的函数,windowed,它接受一个序列,返回一个连续元素块的序列。

C# 是否具有与 LINQ 相同的开箱即用功能?

【问题讨论】:

  • 提供接受的答案的用户承认它是错误的,您现在可能要考虑选择另一个。

标签: c# linq f# window-functions


【解决方案1】:

John Palmer 的回答很好,下面是一个基于他回答的例子。

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

您可能(或不)想将 ToArray() 添加到末尾,没有 ToArray,返回类型仍然在 F# 世界中(Sequence)。有了 ToArray,就回到了 C# 世界(数组)。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 这里,这里,一直都在这样做! Seq.singleton,FSharpSet,你的名字。当我必须用 C# 编写代码时,我经常会使用 F# 标准库。没有它我怎么活!
    • 其实是SeqModule.Windowed。
    • 我喜欢在这里找到我已经投票和编辑但完全忘记的答案。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      您总是可以自己滚动(或从 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# 使用了 Unchecked 数组。:

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

      【讨论】:

      • 将对ArrayInit 的调用替换为var arrR = new T[windowSize]; for (int j = 0; j &lt; windowSize; j++) { arrR[j] = arr[(i + j) % windowSize]; },将r = r - 1 替换为r-- 并且——无论如何在我的测试中——它比Seq.windowed 稍快。
      • Seq.windowed 使用zeroCreateUnchecked,但它只是跳过了size 参数的验证(即if size &lt; 0 then invalidArg ...)。它不会避免边界检查。我相信这是由 JITer 自行决定的。
      • @Daniel,很高兴看到有人上钩了 :) 不过我无法得到你的结果。如果我做var list = Enumerable.Range(0, 100000); var sw = Stopwatch.StartNew(); int count = list.Windowed(15).Count(); sw.Stop(); 然后用Microsoft.FSharp.Collections.SeqModule.Windowed 做同样的事情(在一个新的范围内),C# 总是需要大约两倍的时间......
      • 您是否在调试器中运行 C#?在发布模式下运行这两个版本。
      • @Daniel,应该想到这一点!虽然 C# 对我来说仍然比 F# 慢,但不是两倍。 pastebin.com/TVgfFahp
      猜你喜欢
      • 1970-01-01
      • 2012-03-21
      • 2011-06-13
      • 1970-01-01
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-20
      相关资源
      最近更新 更多