您可以访问IEnumerable吗?

时间:2012-04-21 03:11:45

标签: c# ienumerable yield-return

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

我正在努力学习yield return的细节。现在我在函数中使用了List<int> primes。但我通过yield return返回相同的数据。所以我的问题是

  

我可以访问IEnumerable&lt; int&gt;从我在创建它的功能内部?所以我可以删除List&lt; int&gt;完全是素数。

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

2 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分: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.

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