문제

일부 기준을 기반으로 일부 요소를 선택하고 계층 적 객체를 재귀 적으로 열거하려면 "평평한"과 같은 기술의 예제가 여기에있는 것과 마찬가지로 LINQ를 사용하여 필터링합니다.

링크 텍스트

그러나 양식의 Controls 모음 또는 TreeView의 노드 모음과 같은 것을 열거 할 때,이 유형의 기술을 사용할 수 없었습니다. 수집 : someform.controls를 통과하는 것은 컴파일되지 않습니다.

내가 찾은 가장 유용한 것은 다음과 같습니다.

링크 텍스트

Ienumerable 결과를 가진 ControlCollection을위한 확장 방법을 제공합니다. 그런 다음 LINQ와 함께 사용할 수 있습니다.

위의 예제를 수정하여 문제없이 트리 뷰의 노드를 구문 분석했습니다.

public static IEnumerable<TreeNode> GetNodesRecursively(this TreeNodeCollection nodeCollection)
{
    foreach (TreeNode theNode in nodeCollection)
    {
        yield return theNode;

        if (theNode.Nodes.Count > 0)
        {
            foreach (TreeNode subNode in theNode.Nodes.GetNodesRecursively())
            {
                yield return subNode;
            }
        }
    }
}

이것은 확장 방법을 사용하여 지금 작성하는 코드의 종류입니다.

    var theNodes = treeView1.Nodes.GetNodesRecursively();

    var filteredNodes = 
    (
        from n in theNodes
            where n.Text.Contains("1")
                select n
    ).ToList();

그리고 제약 조건이 전달되는 곳에서 더 우아한 방법이있을 수 있다고 생각합니다.

그러한 절차를 일반적으로 정의 할 수 있는지 알고 싶은 것 : 런타임에 컬렉션 유형과 실제 컬렉션 유형을 일반 매개 변수로 전달할 수 있으므로 코드는 독립적이지 여부와 독립적입니다. 그것은 treenodecollection 또는 controls.collection입니다.

또한 두 번째 링크 (위)에 표시된 것보다 다른 방법 (위의)에 표시된 다른 방법이 있는지 아는 것이 관심이 있습니다.

Leppie의 'Selectmany에 대한 SOLES (위)에 링크 된 게시물에 대한 의견은 단서처럼 보입니다.

Selectmany에 대한 나의 실험은 다음과 같습니다. 글쎄, 그들을 "재해"라고 부릅니다. :)

포인터를 감사합니다. 나는 모든 포스트를 읽는 데 몇 시간을 보냈다. 나는이 영역에 대한 접촉을 발견하고 "y-combinator"와 같은 이국에 들어가는 것을 발견 할 수있다. "겸손한"경험, 나는 추가 할 수 있습니다 :)

도움이 되었습니까?

해결책

이 코드는 트릭을 수행해야합니다

public static class Extensions
{
    public static IEnumerable<T> GetRecursively<T>(this IEnumerable collection,
        Func<T, IEnumerable> selector)
    {
        foreach (var item in collection.OfType<T>())
        {
            yield return item;

            IEnumerable<T> children = selector(item).GetRecursively(selector);
            foreach (var child in children)
            {
                yield return child;
            }
        }
    }
}

다음은 사용 방법의 예입니다

TreeView view = new TreeView();

// ...

IEnumerable<TreeNode> nodes = view.Nodes.
    .GetRecursively<TreeNode>(item => item.Nodes);

업데이트: Eric Lippert의 게시물에 대한 응답으로.

다음은 논의 된 기술을 사용하여 훨씬 개선 된 버전입니다. 반복자에 관한 모든 것.

public static class Extensions
{
    public static IEnumerable<T> GetItems<T>(this IEnumerable collection,
        Func<T, IEnumerable> selector)
    {
        Stack<IEnumerable<T>> stack = new Stack<IEnumerable<T>>();
        stack.Push(collection.OfType<T>());

        while (stack.Count > 0)
        {
            IEnumerable<T> items = stack.Pop();
            foreach (var item in items)
            {
                yield return item;

                IEnumerable<T> children = selector(item).OfType<T>();
                stack.Push(children);
            }
        }
    }
}

다음을 사용하여 간단한 성능 테스트를했습니다 벤치마킹 기술. 결과는 스스로를 말합니다. 나무의 깊이는 두 번째 솔루션의 성능에 한계적인 영향을 미칩니다. 첫 번째 솔루션의 경우 성능이 빠르게 감소하고 결국에는 StackOverflowException 나무의 깊이가 너무 커질 때.

benchmarking

다른 팁

당신은 올바른 길을 가고있는 것 같고 위의 답변에는 좋은 아이디어가 있습니다. 그러나이 모든 재귀 솔루션에는 약간의 결함이 있습니다.

문제의 트리에 최대 트리 깊이가 d <= n 인 총 N 노드가 있다고 가정 해 봅시다.

먼저, 그들은 나무 깊이에 시스템 스택 공간을 소비합니다. 트리 구조가 매우 깊다면 스택을 날려 프로그램을 충돌시킬 수 있습니다. 나무 깊이 d는 나무의 분기 계수에 따라 O (lg n)입니다. 더 나쁜 경우는 분기가 전혀 없으며 링크 된 목록만으로 수백 개의 노드 만있는 트리가 스택을 날려 버릴 것입니다.

둘째, 여기서하고있는 일은 반복자를 호출하는 반복기를 호출하는 반복기를 구축하는 것입니다. 따라서 상단 반복자의 모든 movenext ()가 실제로 다시 O (d)의 비용이 든 일련의 통화를 수행하도록합니다. 모든 노드 에서이 작업을 수행하면 통화의 총 비용은 O (ND)이며 최악의 경우 O (N^2) 및 최상의 경우 O (N LG N)입니다. 둘 다보다 잘할 수 있습니다. 이것이 제 시간에 선형이 될 수없는 이유는 없습니다.

요령은 작고 깨지기 쉬운 시스템 스택 사용을 중단하여 다음에해야 할 일을 추적하고 힙 올라스팅 스택을 사용하여 명시 적으로 추적하는 것입니다.

당신은 당신의 독서 목록에 wes dyer의 기사를 추가해야합니다.

https://blogs.msdn.microsoft.com/wesdyer/2007/03/23/all-about-ITerators/

그는 재귀 반복자를 작성하는 데 좋은 기술을 제공합니다.

Treenodes에 대해서는 잘 모르겠지만 사용하여 Ienumerable 형식의 컨트롤 모음을 만들 수 있습니다. System.Linq 그리고 예를 들어

var ts = (from t in this.Controls.OfType<TextBox>
                 where t.Name.Contains("fish")
                 select t);
//Will get all the textboxes whose Names contain "fish"

내 머리 꼭대기 에서이 재귀를 만드는 방법을 모르겠다 고 말해서 죄송합니다.

Mrydengren의 솔루션을 기반으로 :

public static IEnumerable<T> GetRecursively<T>(this IEnumerable collection,
    Func<T, IEnumerable> selector,
    Func<T, bool> predicate)
{
    foreach (var item in collection.OfType<T>())
    {
        if(!predicate(item)) continue;

        yield return item;

        IEnumerable<T> children = selector(item).GetRecursively(selector, predicate);
        foreach (var child in children)
        {
            yield return child;
        }
    }
}


var theNodes = treeView1.Nodes.GetRecursively<TreeNode>(
    x => x.Nodes,
    n => n.Text.Contains("1")).ToList();

편집 : Billw의 경우

나는 당신이 이런 것을 요구하고 있다고 생각합니다.

public static IEnumerable<T> <T,TCollection> GetNodesRecursively(this TCollection nodeCollection, Func<T, TCollection> getSub)
 where T, TCollection: IEnumerable
{   
    foreach (var theNode in )
    {
        yield return theNode;
        foreach (var subNode in GetNodesRecursively(theNode, getSub))
        {
            yield return subNode;
        }
    }
}

var all_control = GetNodesRecursively(control, c=>c.Controls).ToList();
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top