2010-07-27 2 views
4

yield break 문은 현재 열거자를 벗어나는 경우를 제외하고는 잘 작동하는 다음 메서드가 있습니다. 이것이 왜 그런지 이해하지만 재귀 적 스택을 통해 yield break를 propogate하는 방법에 대해 비어 있습니다.yield break를 사용하여 반복적 인 IEnumerable <T> 루프에서 벗어나는 방법은 무엇입니까?

Top 2 aa 
Top 2 ab 
Top 2 ac 

: 나는 다음 노드를 가지고 통과 할 때

private static IEnumerable<Node> FindChildrenById(IEnumerable nodes, string parentText) { 
     var en = nodes.GetEnumerator(); 
     var targetFound = false; 
     while (en.MoveNext()) { 
      var node = en.Current as Node; 
      if (node != null) 
      { 
       if (node.Parent == null && string.IsNullOrEmpty(parentText)) 
       { 
        //Returns the top level nodes if an empty parentIdis entered 
        targetFound = true; 
        yield return node; 
       } 
       else if (node.Parent != null && node.Parent.Text == parentText) 
       { 
        //returns the nodes belonging to the parent 
        yield return node; 
       } 
       else 
       { 
        //Recurse into the children to see whether one of these is the node to find 
        foreach (var nd in FindChildrenById(node.Nodes, parentText)) 
        { 
         yield return nd; 
        } 
       } 
      } 
     } 
     if (targetFound) 
     { 
      yield break; 
     } 
    } 

그래서 "최고 2"는 parentText ...

Top 1 
    Top 1 a 
    Top 1 b 
Top 2 
    Top 2 a 
     Top 2 aa 
     Top 2 ab 
     Top 2 ac 
    Top 2 b 
Top 3 
    Top 3 a 
    Top 3 b 
Top 4 

로 ... 그때는 결과를 얻을 수 이것은 올바른 결과입니다. 그러나 코드를 단계별로 실행하면 가장 바깥 쪽 루프가 Top 3 및 Top 4를 계속 처리합니다.이 외부 루프에서 어떻게 벗어나나요?

답변

2

것 같아요 아래 코드는 해결됩니다 문제

private static IEnumerable<Node> FindChildrenById(IEnumerable nodes, string parentText) 
    { 
     var result = 
       (from node in nodes 
       where (node.Parent == null && string.IsNullOrEmpty(parentText)) 
         || (node.Parent != null && node.Parent.Text == parentText) 
       select node).TakeWhile(node => !(node.Parent == null && string.IsNullOrEmpty(parentText))); 
     return result; 
    } 

두 가지 확장 방법 (아래 참조)과 s 대상이 충족되는 조건을 찾아 낼 때까지 hould 만 반복

public static class IEnumerablExtensions 
     { 
      //Will iterate the graph in depth first order 
      public static IEnumerable<TResult> Select<TResult>(this IEnumerable collection, Func<Node, TResult> selector) 
      { 
       foreach (var obj in collection) 
       { 
        var node = obj as Node; 
        if (node != null) 
        { 
         yield return selector(node); 
         foreach (var n in node.Nodes.Select(selector)) 
         { 
          yield return n; 
         } 
        } 
       } 
      } 

     public static IEnumerable<Node> Where(this IEnumerable collection, Predicate<Node> pred) 
     { 
      foreach (var node in collection.Select(x => x)) //iterate the list in graph first order 
      { 
       if (pred(node)) 
        yield return node; 
      } 
     } 
    } 

편집 : 지금

수정되어 원래의 게시물의 선택 방법에 오류 (이 아이의 자식을 반복하지 않았다)는 있었다
+0

감사합니다 @ 룬 FS. 나는 이것을 시도하고 다시보고 할 것이다. –

1
//Returns the top level nodes if an empty parentIdis entered 
targetFound = true; 
yield return node; 
yield break; 

그게 효과가 있습니까?

업데이트 : 나는 그것을 좀 더 생각을 준

. 이것은 재귀와 함께 까다로울 수 있습니다. 모든 루프에서 빠져 나오려면 일부 상태 변수를 유지해야합니다.

C#에 꼬리 - 재귀가있는 경우 코드를 CPS으로 변환하는 것이 좋습니다.

당신은 항상 그렇지 않으면 내가 어떤 재귀 일이 일어나고 볼 수없는, MSIL :

+0

나는 당신을 * 모든 곳 *에서 볼 것을 맹세한다. 보통 내가하기 직전에 묻고 답한다.무서운 : D –

+0

@Rei Miyasaka : 항상 그렇진 않지만,'재미 '의 일부 버스트가 있습니다. :) – leppie

1

내가 함수가 실제로 FindChildrenById 이름이 있으리라 믿고있어 그것을 작성할 수 있습니다.

이 가정에서는 예외를 사용하거나 (내가 강력히 권장하는) bool 부분이 초기 단계의 체인 연결을 나타내는 데 사용되는 KeyValuePair<bool, IEnumerable<Node>>을 반환 할 수 있습니다.

그런 다음 API 수준에서 IEnumerable<Node> 부분을 반환하는 래퍼 메서드를 노출하고 bool 부분을 던졌습니다.

public class Node 
{ 
    List<Node> children; 
    public string Text { get; set; } 
    public List<Node> Children { get { return children ?? (children = new List<Node>()); } } 
} 

당신은 통과와 같이 바로 가기 수 있습니다 : 여기

클래스 Node 주어진 예제가 나는 코드의 권리를 가지고있는 경우

public class NodeTraverser 
{ 
    private static KeyValuePair<bool, IEnumerable<Node>> GetChildrenById(string text, Node node) 
    { 
     if(node.Text == text) 
     { 
      return new KeyValuePair<bool,IEnumerable<Node>>(true, node.Children); 
     } 
     foreach(var child in node.Children) 
     { 
      var result = GetChildrenById(text, child); 
      if(result.Key) 
      { 
       return result; // early out 
      } 
     } 
     return new KeyValuePair<bool,IEnumerable<Node>>(false, Enumerable.Empty<Node>()); 
    } 

    public static IEnumerable<Node> FindChildrenbyId(string text, Node root) 
    { 
     return GetChildrenById(text, root).Value; 
    } 
} 
+0

예, 죄송합니다. 제 코드에서 theat little bug를 수정했지만 질문에 이전 버전을 붙여 넣었습니다. 감사. 나는 KeyValuePair를 반환 할 수 있고 여전히 yield 문을 사용할 수 있다고 생각하지 않습니다. IEnumerable을 반환하는 메서드 내에서 발생해야합니다.

+0

내가 의미하는 바를 예제로 추가했습니다. yield 문이 반드시 필요한 것은 아닙니다. 그렇습니까? – corvuscorax

+0

예,이 경우. 나는 변환되고 조작되는 엄청난 수의 노드를 다루고있다. yield return과 yield break를 사용하여 나는 내가 찾고있는 것을 찾았을 때 나머지를 변환하거나 조작하지 않고 루프에서 빠져 나올 수있다. 예를 들어 처리가 수행되기 전에 전체 콜렉션이 사전로드되고 변환되고 조작되어야합니다. 그러나 어쨌든 고마워. –

관련 문제