The implementation of iterators in C# and its consequences (part 2)
Now that you havethe basic idea behind iteratorsunder your belt, you can already answer some questionson iterator usage.Here’s a scenario based on actual events:
I have an iterator that is rather long and complicated,so I’d like to refactor it.For illustrative purposes, let’s say that the enumeratorcounts from 1 to 100 twice.(In real life, of course, the iterator will not be this simple.)
IEnumerable<int> CountTo100Twice() { int i; for (i = 1; i <= 100; i++) { yield return i; } for (i = 1; i <= 100; i++) { yield return i; } }As we learned inProgramming 101,we can pull common code into a subroutine and call the subroutine.But when I do this, I get a compiler error:
IEnumerable<int> CountTo100Twice() { CountTo100(); CountTo100(); }void CountTo100() { int i; for (i = 1; i <= 100; i++) { yield return i; } }
What am I doing wrong?How can I move the “count to 100” into a subroutineand call it twice from the
CountTo100Twice
function?
As we saw last time,iterators are not coroutines.The technique above would have worked great had webuilt iterators out of, say, fibersinstead of building them out of state machines.As state machines, all yield return
statementsmust occur at the “top level”.So how do you iterate with the help of subroutines?
You make the subroutine its own iteratorand suck the results out from the main function:
IEnumerable<int> CountTo100Twice() { foreach (int i in CountTo100()) yield return i; foreach (int i in CountTo100()) yield return i; }IEnumerable<int> CountTo100() { for (i = 1; i <= 100; i++) { yield return i; } }
Exercise:Consider the following fragment:
foreach (int i in CountTo100Twice()) { … }
Explain what happens on the 150th call toMoveNext()
in the above loop.Discuss its consequences for recursive enumerators(such as tree traversal).
0 comments