Get next N elements from enumerable

后端 未结 10 1493
遥遥无期
遥遥无期 2020-12-16 17:38

Context: C# 3.0, .Net 3.5
Suppose I have a method that generates random numbers (forever):

private static IEnumerable RandomNumberGenerator(         


        
相关标签:
10条回答
  • 2020-12-16 18:18

    I think the use of Slice() would be a bit misleading. I think of that as a means to give me a chuck of an array into a new array and not causing side effects. In this scenario you would actually move the enumerable forward 10.

    A possible better approach is to just use the Linq extension Take(). I don't think you would need to use Skip() with a generator.

    Edit: Dang, I have been trying to test this behavior with the following code

    Note: this is wasn't really correct, I leave it here so others don't fall into the same mistake.

    var numbers = RandomNumberGenerator();
    var slice = numbers.Take(10);
    
    public static IEnumerable<int> RandomNumberGenerator()
    {
        yield return random.Next();
    }
    

    but the Count() for slice is alway 1. I also tried running it through a foreach loop since I know that the Linq extensions are generally lazily evaluated and it only looped once. I eventually did the code below instead of the Take() and it works:

    public static IEnumerable<int> Slice(this IEnumerable<int> enumerable, int size)
    {
        var list = new List<int>();
        foreach (var count in Enumerable.Range(0, size)) list.Add(enumerable.First());
        return list;
    }
    

    If you notice I am adding the First() to the list each time, but since the enumerable that is being passed in is the generator from RandomNumberGenerator() the result is different every time.

    So again with a generator using Skip() is not needed since the result will be different. Looping over an IEnumerable is not always side effect free.

    Edit: I'll leave the last edit just so no one falls into the same mistake, but it worked fine for me just doing this:

    var numbers = RandomNumberGenerator();
    
    var slice1 = numbers.Take(10);
    var slice2 = numbers.Take(10);
    

    The two slices were different.

    0 讨论(0)
  • 2020-12-16 18:21

    Are Skip and Take of any use to you?

    Use a combination of the two in a loop to get what you want.

    So,

    list.Skip(10).Take(10);
    

    Skips the first 10 records and then takes the next 10.

    0 讨论(0)
  • 2020-12-16 18:21

    I had made some mistakes in my original answer but some of the points still stand. Skip() and Take() are not going to work the same with a generator as it would a list. Looping over an IEnumerable is not always side effect free. Anyway here is my take on getting a list of slices.

        public static IEnumerable<int> RandomNumberGenerator()
        {
            while(true) yield return random.Next();
        }
    
        public static IEnumerable<IEnumerable<int>> Slice(this IEnumerable<int> enumerable, int size, int count)
        {
            var slices = new List<List<int>>();
            foreach (var iteration in Enumerable.Range(0, count)){
                var list = new List<int>();
                list.AddRange(enumerable.Take(size));
                slices.Add(list);
            }
            return slices;
        }
    
    0 讨论(0)
  • 2020-12-16 18:25

    Using Skip and Take would be a very bad idea. Calling Skip on an indexed collection may be fine, but calling it on any arbitrary IEnumerable<T> is liable to result in enumeration over the number of elements skipped, which means that if you're calling it repeatedly you're enumerating over the sequence an order of magnitude more times than you need to be.

    Complain of "premature optimization" all you want; but that is just ridiculous.

    I think your Slice method is about as good as it gets. I was going to suggest a different approach that would provide deferred execution and obviate the intermediate array allocation, but that is a dangerous game to play (i.e., if you try something like ToList on such a resulting IEnumerable<T> implementation, without enumerating over the inner collections, you'll end up in an endless loop).

    (I've removed what was originally here, as the OP's improvements since posting the question have since rendered my suggestions here redundant.)

    0 讨论(0)
提交回复
热议问题