2011-09-26 2 views
1

이것은 사용자 오류 일 수 있습니다. (나는 다소 기대하고 있습니다). C#에서 이상한 경우를 실행 중입니다. yield를 사용하는 메소드에서 재귀 호출을 시도하면 존중하지 않는 것입니다 (즉 호출이 무시 됨).yield를 사용하는 메서드는 자신을 호출 할 수 없습니다.

// node in an n-ary tree 
class Node 
{ 
    public string Name { get; set; } 
    public List<Node> ChildNodes { get; set; } 
} 
class Program 
{ 
    // walk tree returning all names 
    static IEnumerable<string> GetAllNames(IEnumerable<Node> nodes) 
    { 
     foreach (var node in nodes) 
     { 
      if (node.ChildNodes != null) 
      { 
       Console.WriteLine("[Debug] entering recursive case"); 
       // recursive case, yield all child node names 
       GetAllNames(node.ChildNodes); 
      } 
      // yield current name 
      yield return node.Name; 
     } 
    } 

    static void Main(string[] args) 
    { 
     // initalize tree structure 
     var tree = new List<Node> 
         { 
          new Node() 
           { 
            Name = "One", 
            ChildNodes = new List<Node>() 
                { 
                 new Node() {Name = "Two"}, 
                 new Node() {Name = "Three"}, 
                 new Node() {Name = "Four"}, 
                } 
           }, 
          new Node() {Name = "Five"} 
         }; 

     // try and get all names 
     var names = GetAllNames(tree); 

     Console.WriteLine(names.Count()); 
      // prints 2, I would expect it to print 5 

    } 
} 

답변

2

당신은 재귀 호출의 결과를 반환되지 않습니다

다음 프로그램이 보여줍니다.

foreach(var x in GetAllNames(node.ChildNodes)) 
    yield return x; 
3

당신은 통화를하지만, 그것으로 아무것도하지 않고있다 :

당신은 yield return로 호출에서 반환 된 각 항목이 필요합니다. 여기

static IEnumerable<string> GetAllNames(IEnumerable<Node> nodes) { 
    foreach (var node in nodes) { 
     if (node.ChildNodes != null) { 
      foreach (var childNode in GetAllNames(node.ChildNodes)) { 
       yield return childNode; 
      } 
     } 
     yield return node.Name; 
    } 
} 
0

이 임의로 깊은 구조에 대한 자원 활용의 많은 초래할 수있는 매우 흥미로운 문제가 실제로 결과를 사용해야합니다. 나는 Skeet 씨가 '편평한'기술을 제안했지만 나는 그 링크를 기억하지 않는다고 생각한다.

public static class IEnumerableExtensions 
{ 
    /// <summary> 
    /// Visit each node, then visit any child-list(s) the node maintains 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="subjects">IEnumerable to traverse/></param> 
    /// <param name="getChildren">Delegate to get T's direct children</param> 
    public static IEnumerable<T> PreOrder<T>(this IEnumerable<T> subjects, Func<T, IEnumerable<T>> getChildren) 
    { 
     if (subjects == null) 
      yield break; 

     // Would a DQueue work better here? 
     // A stack could work but we'd have to REVERSE the order of the subjects and children 
     var stillToProcess = subjects.ToList(); 

     while (stillToProcess.Any()) 
     { 
      // First, visit the node 
      T item = stillToProcess[0]; 
      stillToProcess.RemoveAt(0); 
      yield return item; 

      // Queue up any children 
      if (null != getChildren) 
      { 
       var children = getChildren(item); 
       if (null != children) 
        stillToProcess.InsertRange(0, children); 
      } 
     } 
    } 
} 

이 재귀 중첩 반복자를 많이 방지 : 여기에 우리가 자신의 아이디어를 기반으로 사용하는 코드입니다 (이는 IEnumerable에 확장 방법이다).

// try and get all names 
var names = tree.PreOrder<Node>(n => n.ChildNodes); 

지금이 노드 이름이 먼저 온다 '예약 주문'입니다,하지만 당신이 원하는 거라면 쉽게 후 주문을 쓸 수 있습니다 : 당신은 다음을 부를 것이다.

관련 문제