f3l*_*lix 48 .net c# ienumerable iterator
如果我有一个IEnumerable像:
string[] items = new string[] { "a", "b", "c", "d" };
Run Code Online (Sandbox Code Playgroud)
我想循环通过所有连续项目(大小为2的滑动窗口).这将是
("a","b"), ("b", "c"), ("c", "d")
Run Code Online (Sandbox Code Playgroud)
我的解决方案就是这样
public static IEnumerable<Pair<T, T>> Pairs(IEnumerable<T> enumerable) {
IEnumerator<T> e = enumerable.GetEnumerator(); e.MoveNext();
T current = e.Current;
while ( e.MoveNext() ) {
T next = e.Current;
yield return new Pair<T, T>(current, next);
current = next;
}
}
// used like this :
foreach (Pair<String,String> pair in IterTools<String>.Pairs(items)) {
System.Out.PrintLine("{0}, {1}", pair.First, pair.Second)
}
Run Code Online (Sandbox Code Playgroud)
当我编写这段代码时,我想知道.NET框架中是否已经存在执行相同操作的函数,并且它不仅适用于对,而且适用于任何大小的元组.恕我直言应该有一个很好的方法来做这种滑动窗口操作.
我使用C#2.0,我可以想象使用C#3.0(w/LINQ)有更多(更好)的方法来做到这一点,但我主要对C#2.0解决方案感兴趣.不过,我也很欣赏C#3.0解决方案.
Ian*_*cer 50
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));
Run Code Online (Sandbox Code Playgroud)
dah*_*byk 37
而不是需要一个元组(对)类型,为什么不接受一个选择器:
public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> 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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果您愿意,可以跳过中间对象:
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);
Run Code Online (Sandbox Code Playgroud)
或者您可以使用匿名类型:
var pairs = items.Pairwise((x, y) => new { First = x, Second = y });
Run Code Online (Sandbox Code Playgroud)
bra*_*ing 12
最简单的方法是使用ReactiveExtensions
using System.Reactive;
using System.Reactive.Linq;
Run Code Online (Sandbox Code Playgroud)
并使自己成为一个扩展方法,以便将它们放在一起
public static IEnumerable<IList<T>> Buffer<T>(this IEnumerable<T> seq, int bufferSize, int stepSize)
{
return seq.ToObservable().Buffer(bufferSize, stepSize).ToEnumerable();
}
Run Code Online (Sandbox Code Playgroud)
为方便起见,这里是@dahlbyk 答案的无选择器版本。
public static IEnumerable<Tuple<T, T>> Pairwise<T>(this IEnumerable<T> enumerable)
{
var previous = default(T);
using (var e = enumerable.GetEnumerator())
{
if (e.MoveNext())
previous = e.Current;
while (e.MoveNext())
yield return Tuple.Create(previous, previous = e.Current);
}
}
Run Code Online (Sandbox Code Playgroud)
派对有点晚了,但作为所有这些扩展方法的替代方法,可以使用实际的"滑动" Collection来保存(和丢弃)数据.
这是我今天最终制作的一个:
public class SlidingWindowCollection<T> : ICollection<T>
{
private int _windowSize;
private Queue<T> _source;
public SlidingWindowCollection(int windowSize)
{
_windowSize = windowSize;
_source = new Queue<T>(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<T> methods to _source.
}
Run Code Online (Sandbox Code Playgroud)
用法:
int pairSize = 2;
var slider = new SlidingWindowCollection<string>(pairSize);
foreach(var item in items)
{
slider.Add(item);
Console.WriteLine(string.Join(", ", slider));
}
Run Code Online (Sandbox Code Playgroud)
C# 3.0 解决方案(抱歉:)
public static IEnumerable<IEnumerable<T>> Tuples<T>(this IEnumerable<T> sequence, int nTuple)
{
if(nTuple <= 0) throw new ArgumentOutOfRangeException("nTuple");
for(int i = 0; i <= sequence.Count() - nTuple; i++)
yield return sequence.Skip(i).Take(nTuple);
}
Run Code Online (Sandbox Code Playgroud)
这不是世界上性能最高的,但看起来确实令人愉悦。
实际上,使此解决方案成为 C# 3.0 解决方案的唯一因素是 .Skip.Take 构造,因此如果您只是将其更改为将该范围内的元素添加到列表中,那么它对于 2.0 来说应该是黄金。也就是说,它仍然没有性能。
| 归档时间: |
|
| 查看次数: |
11690 次 |
| 最近记录: |