6

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);
        }
Dave Mateer
  • 6,588
  • 15
  • 76
  • 125
  • you can use a clojure syntax or write an object to do the same thing without yield i would think. I don't want to write up an example to fully answer the question. – Paul Nikonowicz Feb 13 '12 at 22:12

3 Answers3

7

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

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();
}
Patrick McDonald
  • 64,141
  • 14
  • 108
  • 120
  • `if F2 is deferred, then F1 is deferred`: In this particular case yes, but not always (see: http://stackoverflow.com/a/17817359/1443490) – cheesemacfly Oct 16 '13 at 19:10
0

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.

Bryan Watts
  • 44,911
  • 16
  • 83
  • 88