2009-06-25 8 views
6

私は木のような構造を持っています。この構造体の各要素は、ルートとなるすべての要素のEnumerableを返すことができる必要があります。このメソッドをIEnumerable<Foo> GetAll()としましょう。 {C, F, G}を返しC要素にGetAllへだから我々はツリー内のネストされた歩留まりのパフォーマンス

 A <-- topmost root 
    / \ 
    B  C 
/\ /\ 
    D E F G 

を持っている場合、コール(要素の固定順序はいいだろうが、必要ありません)。誰もがすでにそれを知っていたと思う。

GetAllの現在の実装では、次のようになります。以前の実装では

public IEnumerable<Foo> GetAll() 
{ 
    yield return this; 

    foreach (Foo foo in MyChildren) { 
     foreach (Foo f in foo.GetAll()) { 
      yield return f; 
     } 
    } 
} 

、私はリストを返し、List.AddRange()を使用して、子-FOOSを追加しました。

私の質問は、利回りを使用しているバージョンが正しく実装されているか、改善が必要なのかどうかです(特にパフォーマンスに関して)。またはこれはちょうど悪いですか?List(またはReadOnlyCollections)に代わってください。

答えて

10

効率的にトラバースする方法を知っている1つのイテレーターではなく、大きな木のイテレーターをたくさん作成することになります。

このに関するいくつかのブログエントリ:

それは、F#は同等のものを持っていることは注目に値します「yield foreach」に「yield!

1

いいえ、それはうまく見えます。

blog entryを見てください、それはいくつかの使用であってもよい:)

-1

利回りを使用して、私の前の経験によると、リストを作成するより多くの、より効果的です。 .NET 3.5を使用している場合は、この実装は問題ありません。しかし、忘れないでください

yield break; 

最後に。 :-)

+0

あの、なぜあなたはこの場合は最後に休憩を得たいですか? –

+2

なぜこれが最後に必要ですか?私は、Enumerableメソッドが終了したときに列挙子が自動的に終了したと考えました... – Bevan

+0

うーん、おそらく私は収量の使用に関して何か誤解しました。私が覚えているように、yield breakでメソッドを閉じなければエラーが出ます。私が何かばかげたと言ったらすみません!その問題を調べるつもりです... – ShdNx

2

より良い解決策は、ツリーを再帰的に横断する訪問メソッドを作成し、それを使用してアイテムを収集することです。(バイナリツリーを仮定して)このような

何か:

public class Node<T> 
{ 
    public void Visit(Action<T> action) 
    { 
     action(this); 
     left.Visit(action); 
     right.Visit(action); 
    } 

    public IEnumerable<Foo> GetAll() 
    { 
     var result = new List<T>(); 
     Visit(n => result.Add(n)); 
     return result; 
    } 
} 

このアプローチを

  • をとるが、ネストされたイテレータ
  • を大量に作成する回避必要
  • より任意の複数のリストを作成回避比較的効率的です
  • lの一部だけが必要な場合は、下に落ちますイスト定期
+0

また、O(1)スペースの代わりにO(n)スペースを占有するため、計算上は効率的ですがメモリは効率的ではありません。 –

+0

ちょうど左右の代わりにforeachを使うべきです。彼はそれがbtreeであるとは特定しなかった。 – Maghis

+0

はい、どのノードにも0以上の子が含まれています。とにかくそれはあまり違いはありません。 – mafu

19

あなたがスタックする再帰アンロール場合にのみ1つのイテレータを持っていますので、あなたは、パフォーマンスを向上させることができます

public IEnumerable<Foo> GetAll() 
{ 
    Stack<Foo> FooStack = new Stack<Foo>(); 
    FooStack.Push(this); 

    while (FooStack.Count > 0) 
    { 
     Foo Result = FooStack.Pop(); 
     yield return Result; 
     foreach (Foo NextFoo in Result.MyChildren) 
      FooStack.Push(NextFoo); 
    } 
} 
+0

ただし、1より大きい... – leppie

+0

ノードごとにイテレータとMyChildrenイテレータを1つのみ生成し、ノードごとにイテレータを生成し、ノードごとに1つのMyChildrenイテレータを生成し、再帰を加えたオリジナルのソリューションを作成しました。 – arbiter

+0

ありがとう、私は実際に自分のコードでこのデザインを使用しました。私は彼のリンクが同じアイデアを含んでいて、彼が以前に投稿して以来、受け入れられたとしてジョンの答えをマークしています。あなたが気にしないことを願っています。 o、o – mafu

関連する問題