1

I've been reading about deferred evaluation of LINQ in the "Programming C#" book by Ian Griffiths. Deferred evaluation was explained through an example in which a Fibonacci method is defined to return a never-ending sequence.

//code 1

static IEnumerable<BigInteger> Fibonacci()
        {
            BigInteger n1 = 1;
            BigInteger n2 = 1;
            yield return n1;
            while (true)
            {
                yield return n2;
                BigInteger t = n1 + n2;
                n1 = n2;
                n2 = t;
            }
        }

Then in Main method only the even numbers of this sequence are retrieved using LINQ:

//code 2

static void Main(string[] args)
        {                                
            var evenFib = from n in Fibonacci()
                          where n % 2 == 0
                          select n;
            foreach (BigInteger n in evenFib)
            {
                Console.WriteLine(n);
                Console.ReadKey();
            }
        }

Next it was explained that due to the deferred evaluation feature of LINQ available by LINQ to Object provider in System.Linq, there would be no problem in printing out the numbers since the numbers are processed in the LINQ only when demanded. However, it compared it to another example in the same chapter in which an extension method of where was defined:

//code 3

    public static class CustomLinqProvider
    {
        public static CultureInfo[] Where(this CultureInfo[] cultures,
        Predicate<CultureInfo> filter)
        {
            return Array.FindAll(cultures, filter);
        }
    }

It stated that if the where method was implemented like this it would only print out a single number cause the where method would never finish its job. So my question is how can I define an extension method of where like the one in code 3 for the Fibonacci example and why would it print out a single number when the where method would never return?

Here is the exact paragraph of the book having the problem I'm referring to, so please check out this one too because I might have misunderstood it (this paragraph explains the Fibonacci example, i.e. code 1 and code 2 by the way):

This will use the Where extension method that LINQ to Objects provides for IEnumerable. If that worked the same way as my CustomLinqProvider class’s Where method for CultureInfo[], this program would never make it as far as printing out a single number. My Where method did not return until it had filtered the whole of its input and produced a fully populated array as its output. If the LINQ to Objects Where method tried that with my infinite Fibonacci enumerator, it would never finish.

Mahsa
  • 362
  • 2
  • 15
  • Why would it never return? You're enumerating over something that has a `yield`, which effectively says "return this value now but come back to where you left off when the next value is requested". If you did something like `ToList` which fully enumerates the enumerable into a List then returns you the list, *then* it would never return because the enumeration doesn't stop – Caius Jard Jul 26 '20 at 08:32
  • Yes but if I use the where method in code 3 then it would never return @CaiusJard – Mahsa Jul 26 '20 at 08:33
  • 2
    If you did something like ToList which fully enumerates the enumerable into a List then returns you the list, then it would never return because the enumeration doesn't stop – Caius Jard Jul 26 '20 at 08:35
  • Yeah that's exactly my question and I also did it. So why this book says it would print out a single number when I use the where method in code 3 while it shouldn't print anything! @CaiusJard – Mahsa Jul 26 '20 at 08:38
  • 1
    It says "If that worked the same way" - in other words, if it tried to find *all* matches before returning anything. And in that case, it would indeed take forever, because you've got an infinite sequence. You can't use the where method in "code 3" for the Fibonacci sequence, because it accepts a `CultureInfo[]`. If you've tried something that you haven't shown, you should show that. – Jon Skeet Jul 26 '20 at 08:39
  • So in that case it wouldn't print anything right? @JonSkeet – Mahsa Jul 26 '20 at 08:40
  • So I answered my own question and explained my solution to it. I would be grateful if you can check that out too. @JonSkeet – Mahsa Jul 26 '20 at 08:48

3 Answers3

4

I defined an extension method as the following and it didn't print out anything. The point is that when it's going to be treated like an array then the where method would never return, hence the evaluation of evenFib would never end and we'll have nothing printed. I also made sure in my code when both LINQ to Object provider and my own extension method are available, my extension method would be used. I didn't change code 1 and code 2 by the way.

public static T[] Where<T>(this IEnumerable<T> src,
        Predicate<T> filter)
        {
            var array = src.ToArray();           
            return Array.FindAll(array,filter);
        }
Mahsa
  • 362
  • 2
  • 15
3

how can I define an extension method of where like the one in code 3 for the Fibonacci example

You can use yield return again if you want deferred execution.

public static IEnumerable<CultureInfo> MyWhere(this IEnumerable<CultureInfo> source, Predicate<CultureInfo> filter) {
    foreach (var thing in source) {
        if (filter(thing)) yield return thing;
    }
}

Note that it has to return and accept IEnumerable<CultureInfo>s because arrays can't be "infinite", IEnumerable<T>s can, because they can use deferred execution.

why would it print out a single number when the where method would never return?

If you try to call your CustomLinqProvider.Where method with an infinite IEnumerable<CultureInfo>, you'd first have to convert that to a CultureInfo[], which you will do by using ToArray. Since the IEnumerable<T> is infinite, ToArray will go on forever. It's not that Where will not return, it's that ToArray will not.

Also, the book says

this program would never make it as far as printing out a single number

"would never make it as far as" means "wouldn't even".

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thanks a lot. I knew I could use the yield return but the main question was if it could print anything or not when using sth similar to code 3. Thanks. – Mahsa Jul 26 '20 at 08:56
  • @Mahsa It could still print things _in general_, it's just that you can't pass in infinite sequences of things, since arrays can't be infinite. – Sweeper Jul 26 '20 at 08:58
1

The paragraph you showed from the book doesn't say it will print a single number. It says it will never print a single number.

[...] this program would never make it as far as printing out a single number [...]

Indeed, deferred execution would allow you to run through an infinite sequence, while exeucting the full sequence without deferred exeuction would never end.

Martin Verjans
  • 4,675
  • 1
  • 21
  • 48