您可以访问 IEnumerable 吗,因为您正在返回它

本文关键字:返回 因为 访问 IEnumerable | 更新日期: 2023-09-27 18:31:54

我下面的代码通过创建素数列表并检查下一个潜在的素数是否可以被列表中的任何素数均匀整除,从而找到number以下的所有素数。

我正在尝试了解yield return的来龙去脉.现在我有一个在函数内部使用的List<int> primes。但是我通过yield return返回相同的数据。所以我的问题是

我可以在创建函数时从函数内部访问 IEnumerable吗?所以我可以完全删除 List素数。

/// <summary>
/// Finds all primes below <paramref name="number"/>
/// </summary>
/// <param name="number">The number to stop at</param>
/// <returns>All primes below <paramref name="number"/></returns>
private static IEnumerable<long> PrimeNumbers(long number)
{
    yield return 2;
    List<long> primes = new List<long>(2);          
    for(long num = 3; num < number; num += 2)
    {
        //if any prime lower then num divides evenly into num, it isn't a prime
        //what I'm doing now
        if(!primes.TakeWhile(x => x < num).Any(x => num % x == 0))
        {
            primes.Add(num);
            yield return num;
        }
        //made-up syntax for what I'd like to do
        if(!this.IEnumerable<long>
                .TakeWhile(x => x < num).Any(x => num % x == 0))
        {
            yield return num;
        }
    }
}

您可以访问 IEnumerable 吗,因为您正在返回它

不,你不能那样做。编译器构建一个状态机来实现yield return,通过枚举枚举的调用代码与代码一样是其工作的一部分。编译器构建一个隐藏对象,用于存储代码的当前状态,包括其调用堆栈和局部变量,并在调用方调用 CurrentMoveNext 时调用方法的不同部分。尝试在另一个枚举正在进行时从头开始枚举对象会弄乱正在进行的枚举,这并不好。

在这种特殊情况下,你也不希望它发生:yield return的实现不存储你生成的值,所以即使你可以在枚举时访问你自己的IEnumerable,它也会递归地多次回调自己来生成每个新项目,所以即使是中等数量的素数也需要花费非常长的时间。

整个枚举器不像List<int> primes那样是一个容器。 这是一个寻找素数的"过程"。 如果你递归地使用自己的过程来生成素数列表来寻找下一个素数,你将一遍又一遍地递归枚举相同的结果,这将非常低效。 考虑一下(如果你真的可以这样做)会发生什么,以找到最多 10 的素数。

yield return 2
num = 3
IEnumerable<long>.TakeWhile(x => x < 3).Any(x => num % x == 0)
    new enumerator
    yield return 2
yield return 3
num = 4
IEnumerable<long>.TakeWhile(x => x < 4).Any(x => num % x == 0)
    new enumerator
    yield return 2
    num = 3
    IEnumerable<long>.TakeWhile(x => x < 3).Any(x => num % x == 0)
        new enumerator
        yield return 2
    yield return 3
num = 5
IEnumerable<long>.TakeWhile(x => x < 5).Any(x => num % x == 0)
    new enumerator
    yield return 2
    num = 3
    IEnumerable<long>.TakeWhile(x => x < 4).Any(x => num % x == 0)
        new enumerator
        yield return 2
        num = 3
        IEnumerable<long>.TakeWhile(x => x < 3).Any(x => num % x == 0)
            new enumerator
            yield return 2
        yield return 3
        num = 4
etc.

这是一个指数级增长的枚举。 这类似于通过计算f(n-1) + f(n-2)天真地找到斐波那契数。 你一遍又一遍地做很多相同的工作,当你达到更高的数字时,更是如此。 内部素数列表用作一种缓存,使枚举非常高效。