2012-04-21 4 views
6

以下のコードは、素数のリストを作成し、次の可能性のある素数がリスト内の任意の素数で割り切れるかどうかを調べることによって、number以下のすべての素数を見つけます。IEnumerableを返すときに、IEnumerableにアクセスできますか?

私はyield returnの機能を学びたいと思っています。今、私はList<int> primesを持っています。しかし、私はyield returnで同じデータを返しています。私の質問は

私はそれを作成中の関数内からIEnumerable < intにアクセスできますか?だから私はリスト< 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; 
     } 
    } 
} 
+0

短い答え:いいえ長い答え:ある数まで素数を得る良い方法があります。 SundaramのSieveをチェックしてください:http://en.wikipedia.org/wiki/Sieve_of_Sundaram – SimpleVar

+0

また、Sieve of Eratosthenesに行くことで改善することができます:http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes Atkinの篩:http://en.wikipedia.org/wiki/Sieve_of_Antkin私は素数を生成するために今日知られている最も速い方法の一つであると考えています(最速でないにしても)。また、擬似コードがwikiにあります開始しました。 – SimpleVar

+0

@YoryeNathanウィキペディアにこれらの篩があるのを見ましたが、もしこのコードをどこか重要なところに置くつもりなら、おそらく私はそれらを使うでしょう。このコードは、「利回りリターン」の考え方をどのように考えるかを訓練するのに役立ちました。 –

答えて

4

いいえ、あなたはそれを行うことはできません。コンパイラはyield returnを実装するステートマシンを構築します。そして、あなたの列挙可能コードを列挙する呼び出しコードは、あなたのコードと同じくらい機能します。コンパイラは、呼び出しスタックとローカルを含むコードの現在の状態を格納する隠しオブジェクトを構築し、呼び出し元がCurrentMoveNextを呼び出すときとは異なるメソッドを呼び出します。別の列挙が進行中の間に最初からオブジェクトを列挙しようとすると、進行中の列挙が乱れてしまいますが、これはうまくいかないでしょう。 yield returnの実装では、生成する値が格納されないため、列挙中に自分のIEnumerableにアクセスできる場合でも、再帰的にコールバックすることになりますそれ自体、新しいアイテムを作るために何回も繰り返されるので、適度な数の素数を生成するにもあなたは馬鹿げた長い時間がかかります。

+0

もちろん可能です。再帰的または入れ子になった列挙型は自動的に処理されるはずですが、それはそれらの美しさの一部です。 'if(!PrimeNumbers(num).Any(x => num%x == 0))return num'を返します。ちょうどLINQPadでテストされています。しかし、あなたの2番目のポイントは、ひどく非効率的です。関数内で 'Console.WriteLine(..) 'を実行するだけで、同じ結果を何回列挙しているかを見ることで、これを知ることができます。 – mellamokb

+1

@mellamokbこれは、シーケンスを作成する関数から*自身*にアクセスしていません。それは、それ自身の状態、ステートマシン、およびその他全てを持つ、それ自身の同一のコピー*にアクセスしています。だからこそ、それが壊れない理由です、そして、それはそれがとても遅い理由です:) – dasblinkenlight

+0

ああ!私は今同じページにいます:)はい、以前の状態はもはや存在しないため、同じインスタンスから前のエントリを取得することはできません。 – mellamokb

2

全体の列挙子は、List<int> primesのようなコンテナではありません。それは素数を見つけるための「プロセス」です。再帰的に独自のプロセスを使用して、素数のリストを生成して次の素数を見つけることができれば、同じ結果を繰り返し再帰的に列挙し、非常に非効率的になります。 (あなたが実際にこれを行うことができれば)それは指数関数的に増加enumartionだ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)を計算することによって、フィボナッチ数を素朴に見つけることに似ています。何度も何度も同じことをやっていますが、さらに高い数字に達するとさらに多くのことが起こります。素数の内部リストは、列挙を非常に効率的にする一種のキャッシュとして機能します。

関連する問題