当前位置:  开发笔记 > 编程语言 > 正文

C#或滑动窗口枚举器中的成对迭代

如何解决《C#或滑动窗口枚举器中的成对迭代》经验,为你挑选了4个好方法。

如果我有一个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解决方案.



1> Ian Mercer..:

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


值得一提的是,这会对"输入"两次进行评估 - 对于数组来说不是问题,但如果它被懒惰地评估可能会很昂贵.
另外,`Zip`的第二个参数可以作为方法组传递:`... input.Zip(input.Skip(1),Tuple.Create);`
我只是在单元测试中做到这一点,只是为了看到差异.使用`Enumerable.Range(0,count)`作为迭代器,我必须在延迟明显之前将计数增加到~100万,并且在它足够慢以至于打扰我之前需要大约1000万.尽管如此,@ dahlbyk的解决方案仍然优雅地避免了这一点,所以我会在任何一天使用它.(扩展方法的全部*点*是能够隐藏不可读的代码,因此这里的优先级应该是直截了当的......).

2> dahlbyk..:

而不是需要一个元组(对)类型,为什么不接受一个选择器:

public static IEnumerable Pairwise(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 });


是的,请参阅C#规范的[第7.4.1节](http://msdn.microsoft.com/en-us/library/aa691335%28v=vs.71%29.aspx).*"在函数成员调用的运行时处理期间,参数列表的表达式或变量引用按从左到右的顺序进行评估,如下所示:..."*
我想知道`yield return ...(previous,previous = ...)`中的执行顺序.C#语言是否保证在计算第二个参数之前准备第一个参数?

3> bradgonesurf..:

最简单的方法是使用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();
}


Rx的互动扩展伴侣(https://www.nuget.org/packages/ix-main)附带一个`IEnumerable `版本的`Buffer()`:https:而不是强制进/出可观察性. //github.com/Reactive-Extensions/Rx.NET/blob/2252cb4edbb25aca12005b9a912311edd2f095f3/Ix.NET/Source/System.Interactive/EnumerableEx.Single.cs#L211-L229

4> Sphinxxx..:

派对有点晚了,但作为所有这些扩展方法的替代方法,可以使用实际的"滑动" 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));
}

推荐阅读
mobiledu2402851203
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有