C# 用自身抵消可枚举项的最佳方法

C# 用自身抵消可枚举项的最佳方法,c#,linq,C#,Linq,给定一个源IEnumerable和一个正整数偏移量以及一个合并函数Func我想生成一个新的IEnumerable 可能的签名如下: public static IEnumerable<TResult> Offset<T, TResult>( this IEnumerable<T> source, int offset, Func<T,T,TResult> selector ); 这里一个非常重要的目标是只枚举一次源IEnumerable。

给定一个源
IEnumerable
和一个正整数偏移量以及一个合并函数
Func
我想生成一个新的
IEnumerable

可能的签名如下:

public static IEnumerable<TResult> Offset<T, TResult>(
    this IEnumerable<T> source, int offset, Func<T,T,TResult> selector
);

这里一个非常重要的目标是只枚举一次源
IEnumerable

好吧,听起来您需要缓冲结果,例如在数组中:

public static IEnumerable<TResult> Offset<T, TResult>(
    this IEnumerable<T> source, int offset, Func<T,T,TResult> selector)
{
    // TODO: Shenanigans to validate arguments eagerly

    T[] buffer = new T[offset];
    using (var iterator = source.GetEnumerator())
    {
        for (int i = 0; i < offset && iterator.MoveNext(); i++)
        {
            buffer[i] = iterator.Current;
        }

        int index = 0;
        while (iterator.MoveNext())
        {
            T old = buffer[index];
            T current = iterator.Current;
            yield return selector(old, current);
            buffer[index] = current;
            index = (index + 1) % offset;
        }
    }
}
公共静态IEnumerable偏移量(
此IEnumerable源、int偏移量、Func选择器)
{
//待办事项:急于验证论点的恶作剧
T[]缓冲区=新的T[偏移];
使用(var iterator=source.GetEnumerator())
{
对于(int i=0;i

这至少适用于您的示例…

您需要跟踪以前的
偏移量
项。
队列
可能是执行此操作的最有效工具。如果所述队列中有足够的项目,则将当前项目与退出队列的项目配对并生成它

public static IEnumerable<TResult> Offset<T, TResult>(
    this IEnumerable<T> source, int offset, Func<T, T, TResult> selector)
{
    var queue = new Queue<T>(offset);
    foreach (var item in source)
    {
        if (queue.Count >= offset)
            yield return selector(queue.Dequeue(), item);

        queue.Enqueue(item);
    }
}
公共静态IEnumerable偏移量(
此IEnumerable源、int偏移量、Func选择器)
{
变量队列=新队列(偏移量);
foreach(源中的var项)
{
如果(queue.Count>=偏移量)
收益返回选择器(queue.Dequeue(),item);
排队。排队(项目);
}
}

第一个值来自哪里?我期望
source.Count()-offset
元素。我想象您可以通过将分子向前移动“offset”次来找到第一个结果值,同时保存源中的第一个枚举值。。。仔细想想,听起来我需要在本地数组中保存元素的偏移量或其他东西你是说在枚举器中保存偏移量?只是手动枚举它。。。所以,也许它是这样工作的,枚举枚举器偏移量的次数并将结果保存到本地队列,然后继续枚举并生成返回选择器。Invoke(queue.deque(),enumerator.Current)@JonSkeet我误解了您的第一个值注释,我认为不需要中的第一个值0,不应该包括在内。我已经更新了这个问题。这比自己显式管理循环缓冲区要容易得多,因为队列本身当然是使用循环缓冲区实现的。@Servy:嗯,它更短。。。我发现它们的可读性差不多。我非常喜欢我的代码中明显存在的两个不同阶段:构建缓冲区,然后生成一对。考虑到循环缓冲区总是固定大小的,而且我们从来没有真正“耗尽”过它,所以数组非常简单。感谢超快的响应,我喜欢使用队列,因为它非常简单。这几乎正是我最终得到的结果,感谢超快的响应
public static IEnumerable<TResult> Offset<T, TResult>(
    this IEnumerable<T> source, int offset, Func<T, T, TResult> selector)
{
    var queue = new Queue<T>(offset);
    foreach (var item in source)
    {
        if (queue.Count >= offset)
            yield return selector(queue.Dequeue(), item);

        queue.Enqueue(item);
    }
}