我使用的方法处理时间较长,需要返回许多结果,但正确的结果可能是返回的任何结果,比如说在 300,000 个结果之后,但其余 700,000 个结果是否正确将在下面检查主要代码:
//a that suppose to return a value at need.
//Main func might need few returns and not all so
static IEnumerable<int> foo() {
//long recusive process, might contain over 1 million results if being asked to yield all.
yield return ret;
}
static void Main(string[] args) {
var a = foo();
while (true) {
var p = a.Take(300); //takes first 300 every loop in the while-loop
foreach (var c in p) {
//does something with it
if (bar == true) //if it is the right one:
goto _break;
}
}
_break:
Console.Read(); //pause
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,代码一次又一次地重新计算 300 返回值。
我怎么可能每次只提取 300 个结果,而不必再次从头开始(使用Skip(n)and then Take(n))并且不将其转换为Collectionwhile 显然将IEnumerable结构保留在函数中foo。
在我开始使用该yield方法之前,我有一个线性非高效的程序,结果比新的程序更快。除了将 的内容分离foo()到外部方法中之外,什么都没有真正改变,这样我就可以一个一个地生成结果,而不是首先将它们全部取出然后再进行处理。然而,表现却相当糟糕。我说的是 300 毫秒到 700 毫秒。我注意到,当要求所有结果 ( foo().ToArray()) 时,它甚至比使用 Yield return 来检查 if 更快bar == true。
所以我想做的是取300->对它们进行采样,如果没有找到->继续取300直到找到。
static void Main(string[] args) {
var a = loly();
while(true){
var p = a.Take(3);
foreach (var c in p) {
Console.Write(c);
if (c==4)
goto _break;
}
}
_break:
Console.Read();
}
static IEnumerable<int> loly() {
var l = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (int i = 0; i < 9; i++) {
yield return l[i];
}
}
Run Code Online (Sandbox Code Playgroud)
这输出:123123123等等
class Program {
static void Main(string[] args) {
var j = 0;
var a = new EnumerationPartitioner<int>(loly().GetEnumerator());
while(true) {
foreach (var c in a.Pull(3)) {
Console.WriteLine(c);
Console.WriteLine("("+(++j)+")");
}
if (a.Ended)
break;
}
foreach (var part in loly().ToInMemoryBatches(7)) {
foreach (var c in part) {
Console.WriteLine(c);
Console.WriteLine("("+(++j)+")");
}
}
Console.Read();
}
static IEnumerable<int> loly() {
var l = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (int i = 0; i < 9; i++) {
yield return l[i];
}
}
}
//Tallseth's method
public static class EnumerationPartitioner {
public static IEnumerable<IEnumerable<T>> ToInMemoryBatches<T>(this IEnumerable<T> source, int batchSize) {
List<T> batch = null;
foreach (var item in source)
{
if (batch == null)
batch = new List<T>();
batch.Add(item);
if (batch.Count != batchSize)
continue;
yield return batch;
batch = null;
}
if (batch != null)
yield return batch;
}
}
//MarcinJuraszek's method
public class EnumerationPartitioner<T> : IEnumerable<T> {
/// <summary>
/// Has the enumeration ended?
/// </summary>
public bool Ended {
get { return over; }
}
public IEnumerator<T> Enumerator { get; private set; }
public EnumerationPartitioner(IEnumerator<T> _enum) {
Enumerator = _enum;
}
/// <summary>
/// Has the enumeration ended
/// </summary>
private bool over = false;
/// <summary>
/// Items that were pulled from the <see cref="Enumerator"/>
/// </summary>
private int n = 0;
/// <summary>
/// Pulls <paramref name="count"/> items out of the <see cref="Enumerator"/>.
/// </summary>
/// <param name="count">Number of items to pull out the <see cref="Enumerator"/></param>
public List<T> Pull(int count) {
var l = new List<T>();
if (over) return l;
for (int i = 0; i < count; i++, n++) {
if ((Enumerator.MoveNext()) == false) {
over = true;
return l;
}
l.Add(Enumerator.Current);
}
return l;
}
/// <summary>
/// Resets the Enumerator and clears internal counters, use this over manual reset
/// </summary>
public void Reset() {
n = 0;
over = false;
Enumerator.Reset();
}
public IEnumerator<T> GetEnumerator() {
return Enumerator;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return Enumerator;
}
}
Run Code Online (Sandbox Code Playgroud)
我需要定期这样做。正如阿列克谢提到的,在处理这种类型的问题时,我想要的是可枚举的东西。
public static IEnumerable<IEnumerable<T>> ToInMemoryBatches<T>(this IEnumerable<T> source, int batchSize)
{
List<T> batch = null;
foreach (var item in source)
{
if (batch == null)
batch = new List<T>();
batch.Add(item);
if (batch.Count != batchSize)
continue;
yield return batch;
batch = null;
}
if (batch != null)
yield return batch;
}
Run Code Online (Sandbox Code Playgroud)