【问题标题】:C# Looping through an IEnumerable for a calculation that uses the n previous and n following elementsC# 循环遍历 IEnumerable 以进行使用前 n 个和后 n 个元素的计算
【发布时间】:2012-08-22 11:07:26
【问题描述】:

我发现自己经常处理一个 IEnumerable 对象,我需要遍历该对象并为每个元素进行计算,该元素取决于紧接在前面和后面的对象。

一个常见的例子是计算滚动平均值,但有时计算比这更复杂,并且依赖于列表中每个元素的多个字段

我永远不确定构建循环的最佳方式。效率很重要,但可维护性和可读性更重要。

  • 有时我会转换为 List,然后使用 for 循环获取元素 [i-1],[i],[i+1],然后执行我的计算。

  • 1234563循环。
  • 我还考虑过使用链表,这样我就可以使用 .Previous 和 .Next 方法。

关于哪种技术最适合使用有什么建议吗?

【问题讨论】:

  • 现在不打算发表意见(简短回答的利弊太多,我没有时间回答长篇大论),但在某些情况下,另一种可能性是您搬入的两个指标串联。 i 开始于例如0 和 j 在 3 处,你用 kij 做一些事情(半开,所以点击 0、1 和 2),然后你移动 ++i;++j; 并再次循环 k , 直到++j > list.Count(所以 j 在最后一次传递时指向列表之外)。

标签: c# list foreach ienumerable


【解决方案1】:

一种选择是制作一个扩展方法,提供一个您可以使用的滚动“窗口”。这将允许您以简单的方式编写循环:

IEnumerable<IList<T>> CreateRollingWindow(IEnumerable<T> items, int size)
{
    LinkedList<T> list = new LinkedList<T>();

    foreach(var item in items)
    {
        list.AddLast(item);
        if (list.Count == size)
        {
            yield return list.ToList();
            list.RemoveFirst();
        }
    }
}

这将使您可以将算法简单地编写为:

foreach(var window as collection.CreateRollingWindow(5))
{
    double rollingAverage = window.Average(); // window is IList<T> here
}

【讨论】:

  • 出于好奇 - 为什么投反对票?
  • 可能有人不理解您的答案...无论如何,我喜欢它通用且可针对不同场景重复使用的事实,但我想知道是否有办法使其更高效...如果您可以假设生成的列表将在下一次迭代之前被使用,您可能可以直接返回它,这样可以避免将数据复制到新列表中。
  • @ThomasLevesque 是的。我特意在 ToList() 调用中使用 LinkedList&lt;T&gt; 而不是 Queue&lt;T&gt; - 但这让它更加通用和安全,因为您可以在延迟执行场景、PLINQ 等中使用它。
【解决方案2】:

这是一个简单的实现:

public static IEnumerable<double> RollingAverage(this IEnumerable<double> values, int count)
{
    var queue = new Queue<double>();
    foreach (var v in values)
    {
        queue.Enqueue(v);
        if (queue.Count == count)
        {
            yield return queue.Average();
            queue.Dequeue();
        }
    }
}

它可能会被改进,但它似乎工作......

编辑:这是一个稍微好一点的版本(它不需要枚举队列来计算平均值):

public static IEnumerable<double> RollingAverage(this IEnumerable<double> values, int count)
{
    var queue = new Queue<double>();
    double sum = 0.0;
    foreach (var v in values)
    {
        sum += v;
        queue.Enqueue(v);
        if (queue.Count == count)
        {
            yield return sum / count;
            sum -= queue.Dequeue();
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 2011-02-10
    • 2022-01-03
    • 2023-04-02
    • 1970-01-01
    • 2015-12-20
    • 2010-12-19
    相关资源
    最近更新 更多