Reputation: 6626
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
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
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
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