gazoh
gazoh

Reputation: 243

Iterate on 2 consecutive values of an IEnumerable

For my application, I have built an iterator and I need to use each of the values it yields along with the previous one.

For example, consider the following iterator, that yields the first terms of the Fibonacci sequence:

public static IEnumerable<int> GetFibonacciNumbers(int count)
{
    int a = 0;
    int b = 1;
    int i = 0;

    while (i < count)
    {
        yield return a;
        int temp = a;
        a = b;
        b = temp + b;
        i++;
    }
}

Now I want to use this enumerator to compute increasingly accurate estimates of the golden ratio, which means I need to use the yielded value along with the previous one. The following approach kind of works:

static void Main(string[] args)
{
    int fib0 = 0;
    foreach (int fib1 in GetFibonacciNumbers(10))
    {
        var phi = (double)fib1 / fib0;
        Console.WriteLine(phi);
        fib0 = fib1;
    }
}

The issues are that the first value for phi is wrong, as the first value for fib0 used isn't actually part of the sequence.

Upvotes: 1

Views: 205

Answers (4)

haim770
haim770

Reputation: 49095

Just return both the current and previous values from your iterator:

public static IEnumerable<(int prevValue, int currentValue)> GetFibonacciNumbers(int count)
{
    int a = 0;
    int b = 1;
    int i = 0;

    while (i < count)
    {
        yield return (a, b);

        int temp = a;
        a = b;
        b = temp + b;
        i++;
    }
}

The above uses C# 7.0 Tuple Syntax, but you can easily convert it to use a regular Tuple<int, int> as well.

Upvotes: 2

Damien_The_Unbeliever
Damien_The_Unbeliever

Reputation: 239646

Haim770's answer is correct if you want to modify your generator. However, you might also want to use a general purpose method that you can then reuse with any IEnumerable<T>:

    public static IEnumerable<(T prevValue, T currentValue)> OverlappingPairs<T>(IEnumerable<T> source)
    {
        bool first = true;
        T previous = default;
        foreach (var item in source)
        {
            if (!first)
                yield return (previous, item);
            first = false;
            previous = item;
        }
    }

(And of course, if you add this before the parameter and put it in a static class, it'll work as an extension method)

Upvotes: 1

Sean
Sean

Reputation: 62472

You can have the enumerating function keep track of the previous and current values and return them as a tuple. For example, in the fibonacci example it would be something like this:

static IEnumerable<(int Previous, int Current)> GetFibonacciNumbers(int count)
{
    var (previous, current) = (0, 1);

    for(int i = 0; i < count; i++)
    {
        yield return (previous, current);

        (previous, current) = (current, previous + current);
    }
}

Upvotes: 1

Jeroen Maassen
Jeroen Maassen

Reputation: 91

Some fibonacci sequences start with 1 and 1 instead of 0 and 1. So maybe that could solve your problem.

Upvotes: 0

Related Questions