THX*_*138 11 .net c# linq enumerator
上下文:C#3.0,.Net 3.5
假设我有一个生成随机数的方法(永远):
private static IEnumerable<int> RandomNumberGenerator() {
while (true) yield return GenerateRandomNumber(0, 100);
}
Run Code Online (Sandbox Code Playgroud)
我需要将这些数字分组为10组,所以我想要像:
foreach (IEnumerable<int> group in RandomNumberGenerator().Slice(10)) {
Assert.That(group.Count() == 10);
}
Run Code Online (Sandbox Code Playgroud)
我已经定义了Slice方法,但我觉得应该已经定义了一个.这是我的Slice方法,仅供参考:
private static IEnumerable<T[]> Slice<T>(IEnumerable<T> enumerable, int size) {
var result = new List<T>(size);
foreach (var item in enumerable) {
result.Add(item);
if (result.Count == size) {
yield return result.ToArray();
result.Clear();
}
}
}
Run Code Online (Sandbox Code Playgroud)
问题:有没有更简单的方法来完成我想要做的事情?也许是Linq?
注意:上面的例子是一个简化,在我的程序中我有一个Iterator,它以非线性的方式扫描给定的矩阵.
编辑:为什么Skip+ Take不好.
实际上我想要的是:
var group1 = RandomNumberGenerator().Skip(0).Take(10);
var group2 = RandomNumberGenerator().Skip(10).Take(10);
var group3 = RandomNumberGenerator().Skip(20).Take(10);
var group4 = RandomNumberGenerator().Skip(30).Take(10);
Run Code Online (Sandbox Code Playgroud)
没有再生数(10 + 20 + 30 + 40)次的开销.我需要一个能产生40个数字的解决方案,然后将4个组中的数字分成10个.
Mic*_*ael 12
在循环中使用两者的组合来获得您想要的.
所以,
list.Skip(10).Take(10);
Run Code Online (Sandbox Code Playgroud)
跳过前10条记录然后接下来的10条记录.
我做了类似的事情.但我希望它更简单:
//Remove "this" if you don't want it to be a extension method
public static IEnumerable<IList<T>> Chunks<T>(this IEnumerable<T> xs, int size)
{
var curr = new List<T>(size);
foreach (var x in xs)
{
curr.Add(x);
if (curr.Count == size)
{
yield return curr;
curr = new List<T>(size);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我认为你的是有缺陷的.为所有块/切片返回相同的数组,因此只有最后一个块/切片才能获得正确的数据.
增加:数组版本:
public static IEnumerable<T[]> Chunks<T>(this IEnumerable<T> xs, int size)
{
var curr = new T[size];
int i = 0;
foreach (var x in xs)
{
curr[i % size] = x;
if (++i % size == 0)
{
yield return curr;
curr = new T[size];
}
}
}
Run Code Online (Sandbox Code Playgroud)
增加: Linq版本(不是C#2.0).正如所指出的那样,它不会对无限序列起作用,并且会比替代方案慢很多:
public static IEnumerable<T[]> Chunks<T>(this IEnumerable<T> xs, int size)
{
return xs.Select((x, i) => new { x, i })
.GroupBy(xi => xi.i / size, xi => xi.x)
.Select(g => g.ToArray());
}
Run Code Online (Sandbox Code Playgroud)
使用Skip和Take将是一个非常糟糕的主意.调用Skip索引集合可能没问题,但是任意调用它都会IEnumerable<T>导致枚举超过跳过的元素数量,这意味着如果你反复调用它,你会在序列上枚举一个数量级更多时间比你需要的多.
抱怨所有你想要的"过早优化"; 但那太荒谬了.
我认为你的Slice方法和它一样好.我打算提出一种不同的方法来提供延迟执行并避免中间数组分配,但这是一个危险的游戏(即,如果你尝试类似于ToList这样的结果IEnumerable<T>实现,而不是枚举内部集合,你最终会陷入无尽的循环中.
(我已经删除了原来的内容,因为自从发布问题后OP的改进已经使我的建议变得多余了.)