简体   繁体   中英

C# - TakeWhile and SkipWhile not returning?

I have a class RekenReeks which returns numbers starting from 2, multiplied by 2. So {2,4,8,16,32,64}

Now I learned about the TakeWhile and SkipWhile methods as well as LINQ.

So I have created 3 variables which should store exactly the same but my Console.WriteLine only prints selection1 and not 2 and 3.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            RekenReeks reeks = new RekenReeks(2, n => n * 2);
            var selection = from i in reeks
                        where i > 10
                        && i < 1000
                        select i;

        var selection2 = reeks.TakeWhile(n => n < 1000 && n > 10);

        var selection3 = reeks.SkipWhile(n => n > 1000 && n < 10);

        foreach (int i in selection)
        {
            Console.WriteLine("selection1 {0}",i);
        }

        foreach (int i in selection2)
        {
            Console.WriteLine("selection2 {0}", i);
        }

        foreach (int i in selection3)
        {
            Console.WriteLine("selection3 {0}", i);
        }

        Console.ReadLine();
    }
}


public class RekenReeks : IEnumerable<int>
{
    Func<int, int> getNew;
    int number;
    public RekenReeks(int starton, Func<int, int> getNewThing)
    {
        getNew = getNewThing;
        number = starton;
    }

    public IEnumerator<int> GetEnumerator()
    {
        yield return number;
        for (; ; )
        {

            yield return getNew(number);
            number = getNew(number);

        }

    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }


}
}

Your sequence is unlimited (theoretically). You assume too much. The functionality of your program cannot possibly know that your sequence is strictly monotonic increasing.

var selection = from i in reeks
                    where i > 10
                    && i < 1000
                    select i;

This will never stop. Because the where will always pull the next value, it does not know it's condition will be satisfied, it always has to check the next value.

    var selection2 = reeks.TakeWhile(n => n < 1000 && n > 10);

This will take values, while they are between 11 and 999. As your sequence starts with 2, it will stop right on the first value.

    var selection3 = reeks.SkipWhile(n => n > 1000 && n < 10);

This will skip values while they are between 11 and 999. As the first is 2, it will skip none and therefore yield all .

Both TakeWhile() and SkipWhile() start from very beginning of the sequence and stop taking/skipping when condition doesn't meet.

  // I've redesigned your ReekenReeks
  // [2, 4, 8, 16, 32, 64, ..., 2**30]
  var reeks = Enumerable
    .Range(1, 30)
    .Select(index => 1 << index);

For instance, if you put

  var selection2 = reeks
    .TakeWhile(n => n < 1000 && n > 10);

since the 1st item of the reeks is 2 the condition n < 1000 && n > 10 doesn't meet TakeWhile stops and returns an empty sequence. The right implemenation is

  var selection2 = reeks
    .SkipWhile(n => n <= 10)
    .TakeWhile(n => n < 1000);

If you want to cut off values from the middle of the sequence ( selection3 ) you have to use Where :

  var selection3 = reeks
    .Where(n => !(n > 1000 && n < 10)); // cut theses items off

Be careful when printing out an infinine sequence, .Take() is a good choice here:

   var selection3 = reeks
    .Where(n => n > 1000 && n < 10)
    .Take(100); // First 100 items in case the sequence is too long
var selection = from i in reeks
  where i > 10
  && i < 1000
  select i;

There's no reason for this to ever end. When i is 1024 it won't be yielded, but it will still check then if 2048 is less than 1000 , or 4096 is less than 1000 , or 8192 is less than 1000 and so on forever (eventually either overflow exception happens or n wraps around to 0 which then keeps being set to 0 * 2 which is still 0 ).

reeks.TakeWhile(n => n < 1000 && n > 10)

The first value tried is 2 . This does not satisfy the predicate n < 1000 && n > 10 because it is not true that 2 > 10 . Therefore the taking stops.

reeks.SkipWhile(n => n > 1000 && n < 10)

There is no value of n for which n > 1000 && n < 10 . Therefore this is the same as having no SkipWhile at all.

It seems like what you want is:

reeks.SkipWhile(n => n >= 1000 || n <= 10).TakeWhile(n => n < 1000 && n > 10)

Which skips until the first number that meets the criteria is found, then takes all that meet it until the first that does not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM