Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does an IEnumerable have to use Yield to be deferred

Tags:

c#

Does an IEnumerable have to use Yield to be deferred?

Here is test code which has helped me understand deferred execution and yield.

 //immediate execution
        public IEnumerable Power(int number, int howManyToShow)
        {
            var result = new int[howManyToShow];
            result[0] = number;
            for (int i = 1; i < howManyToShow; i++)
                result[i] = result[i - 1] * number;
            return result;
        }

        //deferred but eager
        public IEnumerable PowerYieldEager(int number, int howManyToShow)
        {
            var result = new int[howManyToShow];
            result[0] = number;
            for (int i = 1; i < howManyToShow; i++)
                result[i] = result[i - 1] * number;

            foreach (var value in result)
                yield return value;
        }

        //deferred and lazy
        public IEnumerable PowerYieldLazy(int number, int howManyToShow)
        {
            int counter = 0;
            int result = 1;
            while (counter++ < howManyToShow)
            {
                result = result * number;
                yield return result;
            }
        }

        [Test]
        public void Power_WhenPass2AndWant8Numbers_ReturnAnEnumerable()
        {
            IEnumerable listOfInts = Power(2, 8);

            foreach (int i in listOfInts)
                Console.Write("{0} ", i);
        }


        [Test]
        public void PowerYieldEager_WhenPass2AndWant8Numbers_ReturnAnEnumerableOfInts()
        {
            //deferred but eager execution
            IEnumerable listOfInts = PowerYieldEager(2, 8);

            foreach (int i in listOfInts)
                Console.Write("{0} ", i);
        }


        [Test]
        public void PowerYield_WhenPass2AndWant8Numbers_ReturnAnEnumerableOfIntsOneAtATime()
        {
            //deferred and lazy execution
            IEnumerable listOfInts = PowerYieldLazy(2, 8);

            foreach (int i in listOfInts)
                Console.Write("{0} ", i);
        }
like image 517
Dave Mateer Avatar asked Feb 13 '12 22:02

Dave Mateer


2 Answers

It doesn't have to use yield - ultimately you can do everything that yield does, by writing a custom enumerator (IEnumerator[<T>]) and delaying the action until the first MoveNext(). However, that is pretty painful to implement. Certainly if you do use yield, the implementation is deferred by default (you can make it non-deferred by using two methods - one that doesn't use yield, which then after accessing the data uses the other method (an iterator block) to implement the enumerator.

Frankly, writing enumerators is hard and buggy. I avoid it unless absolutely necessary. Iterator blocks are great.

like image 182
Marc Gravell Avatar answered Nov 14 '22 23:11

Marc Gravell


A function (F1) that returns IEnumerable can return an IEnumerable computed in another function (F2), if F2 is deferred, then F1 is deferred

for example, in the following code, both F1 and F2 are deferred

public IEnumerable<int> F2()
{
    for (int i = 0; i < 10; i++) {
        yield return i;
    }
}

public IEnumerable<int> F1()
{
    return F2();
}
like image 29
Patrick McDonald Avatar answered Nov 14 '22 22:11

Patrick McDonald