如果我有一个IEnumerable像:
string[] items = new string[] { "a", "b", "c", "d" };
我想循环通过所有连续项目(大小为2的滑动窗口).这将是
("a","b"), ("b", "c"), ("c", "d")
我的解决方案就是这样
public static IEnumerable> Pairs(IEnumerable enumerable) { IEnumerator e = enumerable.GetEnumerator(); e.MoveNext(); T current = e.Current; while ( e.MoveNext() ) { T next = e.Current; yield return new Pair (current, next); current = next; } } // used like this : foreach (Pair pair in IterTools .Pairs(items)) { System.Out.PrintLine("{0}, {1}", pair.First, pair.Second) }
当我编写这段代码时,我想知道.NET框架中是否已经存在执行相同操作的函数,并且它不仅适用于对,而且适用于任何大小的元组.恕我直言应该有一个很好的方法来做这种滑动窗口操作.
我使用C#2.0,我可以想象使用C#3.0(w/LINQ)有更多(更好)的方法来做到这一点,但我主要对C#2.0解决方案感兴趣.不过,我也很欣赏C#3.0解决方案.
In .NET 4 this becomes even easier:-
var input = new[] { "a", "b", "c", "d", "e", "f" }; var result = input.Zip(input.Skip(1), (a, b) => Tuple.Create(a, b));
而不是需要一个元组(对)类型,为什么不接受一个选择器:
public static IEnumerablePairwise (this IEnumerable source, Func resultSelector) { TSource previous = default(TSource); using (var it = source.GetEnumerator()) { if (it.MoveNext()) previous = it.Current; while (it.MoveNext()) yield return resultSelector(previous, previous = it.Current); } }
如果您愿意,可以跳过中间对象:
string[] items = new string[] { "a", "b", "c", "d" }; var pairs = items.Pairwise((x, y) => string.Format("{0},{1}", x, y)); foreach(var pair in pairs) Console.WriteLine(pair);
或者您可以使用匿名类型:
var pairs = items.Pairwise((x, y) => new { First = x, Second = y });
最简单的方法是使用ReactiveExtensions
using System.Reactive; using System.Reactive.Linq;
并使自己成为一个扩展方法,以便将它们放在一起
public static IEnumerable> Buffer (this IEnumerable seq, int bufferSize, int stepSize) { return seq.ToObservable().Buffer(bufferSize, stepSize).ToEnumerable(); }
派对有点晚了,但作为所有这些扩展方法的替代方法,可以使用实际的"滑动" Collection
来保存(和丢弃)数据.
这是我今天最终制作的一个:
public class SlidingWindowCollection: ICollection { private int _windowSize; private Queue _source; public SlidingWindowCollection(int windowSize) { _windowSize = windowSize; _source = new Queue (windowSize); } public void Add(T item) { if (_source.Count == _windowSize) { _source.Dequeue(); } _source.Enqueue(item); } public void Clear() { _source.Clear(); } ...and just keep forwarding all other ICollection methods to _source. }
用法:
int pairSize = 2; var slider = new SlidingWindowCollection(pairSize); foreach(var item in items) { slider.Add(item); Console.WriteLine(string.Join(", ", slider)); }