Dave Mateer
Dave Mateer

Reputation: 6626

Does an IEnumerable have to use Yield to be deferred

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);
        }

Upvotes: 6

Views: 450

Answers (3)

Bryan Watts
Bryan Watts

Reputation: 45465

Deferred and eager are opposites - lazy is simply a synonym for deferred.

An eager sequence is one which is pre-calculated, such as a list or array. A deferred sequence is one which is calculated whenever it is iterated.

In your examples, Power is eager because it calculates an array and returns it. This is different from PowerYieldEager, which does not build the array until the resulting IEnumerable is iterated.

You can think of a deferred vs. eager as the potential for a sequence vs. the content of a sequence. With that in mind, yield return is only one way of deferring; any sequence calculated when the results are requested is a deferred sequence.

Upvotes: 0

Patrick McDonald
Patrick McDonald

Reputation: 65451

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();
}

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1063501

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.

Upvotes: 7

Related Questions