6

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

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

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

/// <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;
        }
    }
}
4

2 回答 2

4

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

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

于 2012-04-21T03:27:30.620 回答
2

整体枚举器不是像这样的容器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)。你一遍又一遍地做很多相同的工作,当你达到更高的数字时更是如此。素数的内部列表用作一种缓存,使您的枚举非常有效。

于 2012-04-21T03:27:45.047 回答