对 IEnumerable<T> 结果进行分区

Nuc*_*ucS 3 c# loops

我使用的方法处理时间较长,需要返回许多结果,但正确的结果可能是返回的任何结果,比如说在 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)

tal*_*eth 5

我需要定期这样做。正如阿列克谢提到的,在处理这种类型的问题时,我想要的是可枚举的东西。

    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)