Sequence Generation using IEnumerable<T>

Momerath 2 Tallied Votes 625 Views Share

Sometimes we need to generate sequence and perform some process on the results. This usually results in some code like this (We'll be using the Fibonacci sequence in these examples):

void MyMethod() {
    // What we want to do is output to the screen the first 10 Fibonacci numbers
    // multiplied by 5 followed by the first 5 Fibonacci numbers multiplied by 3
    ResetFibSequence();
    for (int i = 0; i < 10; i++) {
        Console.WriteLine(NextFib() * 5);
    }
    ResetFibSequence();
    for (int i = 0; i < 5; i++) {
        Console.WriteLine(NextFib() * 3);
    }
}

private fibCurrent = 0;
private fibNext = 1;

void ResetFibSequence() {
    fibCurrent = 0;
    fibNext = 1;
}

int NextFib() {
    int temp = fibCurrent + fibNext;
    fibCurrent = fibNext;
    fibNext = temp;

    return fibCurrent;
}

This code requires that we have some method to reset the sequence and some class variables to hold the current/next values. It does have the advantage that each number in the sequence is generated only as we need it so memory requirements don't change based on how many of the sequence we need. We could rewrite it using a List<T> to store the values and get rid of some of this code:

void MyMethod2() {
    List<int> fibList = GenerateFib(10);
    foreach (int i in fibList) {
        Console.WriteLine(i * 5);
    }

    fibList = GenerateFib(5);
    foreach (int i in fibList) {
        Console.WriteLine(i * 3);
    }
}

List<int> GenerateFib(int n) {
    List<int> myList = new List<int>();

    int fibCurrent = 1;
    int fibNext = 1;
    int temp;
    while (myList.Count < n) {
        myList.Add(fibCurrent);
        temp = fibCurrent + fibNext;
        fibCurrent = fibNext;
        fibNext = temp;
    }

    return myList;
}

This is better than the first code. No more class variables to store values between calls, no need for a reset method. But we do need to allocate a list. In this case that isn't very important, but what if you needed 1,000,000 numbers in the sequence, or 10,000,000, or more? Then it becomes more of an issue. Also, your user will notice that the program 'pauses' while it generates the list, then starts the output. This might lead them to believe the program has stopped working if the pause is long enough.

How do we improve on this? That's where IEnumerable<T> comes in. Let's rewrite this using IEnumerable<T> and look at the benefits it provides:

void MyMethod3() {
    foreach (int i in FibSequence(10)) {
        Console.WriteLine(i * 5);
    }
    foreach (int i in FibSequence(5)) {
        Console.WriteLine(i * 3);
    }
}

IEnumerable<int> FibSequenece(int count) {
    int fibCurrent = 1;
    int fibNext = 1;
    for (int i = 0; i < count; i++) {
        yield return fibCurrent;
        int temp = fibCurrent + fibNext;
        fibCurrent = fibNext;
        fibNext = temp;
    }
}

Most of this looks similar to the previous code, with the exception of lines 10 and 14. Line 10 tells us that we are going to return an enumerator that has values that are of type int. Line 14 is where the magic happens. What this line does is tell the system to stop processing this method here and yield control back to the calling method. When the next number in the sequence is requested (by the foreach lines) the system knows to begin processing on the line after the yield statement, until it reaches another yield (or there is nothing left to process). This means that each number in the sequence is generated only as we need it with very little overhead even for extremely long sequence requests.

As you can see, using IEnumerable has the benefits of both the other methods: Minimal memory requirements that don't change based on the length of the sequence and no need for additional state class variables to hold values until the next number generation (yes, I know that there are state variables but they are contained within the generation method thus they don't 'pollute' the class).

ddanbe commented: LIke to see more of this! :) +8
ddanbe 2,724 Professional Procrastinator Featured Poster

This should be in the form of a snippet or tutorial!

Momerath 1,327 Nearly a Senior Poster Featured Poster

I submitted it as a tutorial put it didn't seem to take. Let me try it again :)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.