迭代 IEnumerable 的 2 个连续值

Iterate on 2 consecutive values of an IEnumerable

对于我的应用程序,我构建了一个迭代器,我需要将它产生的每个值与前一个一起使用。

例如,考虑以下迭代器,它产生斐波那契数列的第一项:

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++;
    }
}

现在我想使用这个枚举器来计算越来越准确的黄金比例估计值,这意味着我需要将产生的值与前一个一起使用。以下方法有效:

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

问题是 phi 的第一个值是错误的,因为使用的 fib0 的第一个值实际上不是序列的一部分。

一些斐波那契数列以 1 和 1 开头,而不是 0 和 1。所以也许这可以解决您的问题。

您可以让枚举函数跟踪先前值和当前值,并将它们 return 作为元组。例如,在斐波那契示例中,它将是这样的:

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);
    }
}

只需 return 来自迭代器的当前值和先前值:

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++;
    }
}

以上使用 C# 7.0 Tuple Syntax,但您也可以轻松地将其转换为使用常规 Tuple<int, int>

如果您想修改生成器,

是正确的。但是,您可能还想使用一种通用方法,然后可以将其重复用于任何 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;
        }
    }

(当然,如果您在参数前添加this并将其放在静态class中,它将作为扩展方法工作)