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.

3

There are 3 best solutions below

0
Mahsa On

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);
        }
2
Sweeper On

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".

0
Martin Verjans On

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.