Rob*_*lly 7 c# ienumerable foreach list
我发现自己经常处理一个IEnumerable对象,我需要循环执行每个元素的计算,这些元素依赖于前一个和后一个对象的n.
一个常见的例子是计算滚动平均值,但有时计算比这更复杂,并依赖于列表中每个元素的几个字段
我永远不确定构建循环的最佳方法.效率很重要,但可维护性和可读性更重要.
有时我转换为List然后使用for循环来获取元素[i-1],[i],[i + 1],然后执行我的计算.
其他时候我把它保存为IEnumerable,但是我"缓存"前面几个元素,所以我不进行i的计算,直到我在foreach循环中得到[i + 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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这样您就可以将算法编写为:
foreach(var window as collection.CreateRollingWindow(5))
{
double rollingAverage = window.Average(); // window is IList<T> here
}
Run Code Online (Sandbox Code Playgroud)
这是一个简单的实现:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
它可能可以改进,但似乎有效......
编辑:这是一个稍微好一点的版本(它不需要枚举队列来计算平均值):
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)