Как выполнить итерации через древовидную структуру с помощью генератора?

вы ссылаетесь на ту же переменную в своем сервисе редактирования через "this.data"

  public read2(id) {
    this.tempId = id;
    if (this.data.length) {
    //this.data was already initialized during your .read() initialization
    //therefore if(this.data.length) will always be true
    //if you really want to maintain this data structure in your servicem
    //create a differnt property that holds the 2nd data. e.g. "this.data2"
      return super.next(this.data);
    }


    this.fetch2()
      .pipe(
        tap(data => {
          this.data = data;
        })
      )
      .subscribe(data => {
        super.next([data]);
      });
  }

и ниже - настоящая головная боль, выборка выдает массив, который является правильным, однако выборка 2 выдает единственный объект, но вы ожидаем, что он будет загружен в вашу сетку

  private fetch(action: string = '', data?: any): Observable<any[]> {
    return this.http
      .get(`https://jsonplaceholder.typicode.com/posts`)
      .pipe(map(res => <any[]>res));
  }

  private fetch2(action: string = '', data?: any): Observable<any[]> {
    return this.http
      .get(`https://jsonplaceholder.typicode.com/posts/${this.tempId}`)
      .pipe(map(res => <any[]>res));
  }

, поэтому поместите эту строку в ваш ngOninit

this.view = this.editService.pipe(map(data => process(data, this.gridState)),filter(d=>d.total>0),take(1));

, а затем в свой сервис отредактируйте ваш read2

public read2(id) {
    this.tempId = id;
    this.data = [];
    if (this.data.length) {
      return super.next(this.data);
    }


    this.fetch2()
      .pipe(
        tap(data => {
          this.data = data;
        })
      )
      .subscribe(data => {
        //super.next(data); remove this line
        super.next([data]);
      });
  }
5
задан Erik Forbes 30 December 2008 в 22:09
поделиться

3 ответа

Вот то, как необходимо реализовать Ответвление. EnumerateLeaves:

public IEnumerable<ITreeNode> EnumerateLeaves()
{
    foreach( var node in m_treeNodes )
    {
        if( node is Leaf )
            yield return node;
        else
        {
            foreach (ITreeNode childNode in node.EnumerateLeaves())
                yield return childNode;
        }
    }
}
7
ответ дан 13 December 2019 в 19:38
поделиться

Другие ответы корректны, но шаблон наличия возврата урожая в цикле foreach с рекурсивным вызовом сделает время выполнения для итерации через дерево чего-то как O (количество узлов * средняя глубина узла). Дополнительную информацию см. в этом сообщении в блоге о проблеме.

Вот попытка генератора, который эффективен и в и в использовании памяти во время выполнения:

class Node
{
    List<Node> _children;

    public bool IsLeaf { get { return _children.Count == 0; } }

    public IEnumerable<Node> Children { get { return _children; } }

    public IEnumerable<Node> EnumerateLeaves()
    {
        if (IsLeaf)
        {
            yield return this;
            yield break;
        }

        var path = new Stack<IEnumerator<Node>>();

        path.Push(Children.GetEnumerator());

        while(!path.Empty)
        {
            var cur = path.Pop();
            if (cur.MoveNext())
            {
                path.Push(cur);
                if (cur.IsLeaf)
                {
                    yield return cur;
                }
                else
                {
                    path.Push(cur.Children.GetEnumerator());
                }
            }

        }
    }

}
1
ответ дан 13 December 2019 в 19:38
поделиться

lassevk является правильным - одна потенциальная проблема с тем методом, однако, то, что рекурсивно вызов в enumerables может привести к O (n^2) производительность. Если это - проблема, то необходимо вместо этого факторизовать рекурсию и использовать внутренний стек.

public IEnumerable<ITreeNode> EnumerateLeaves()
{
    Stack<ITreeNode> workStack = new Stack<ITreeNode>(m_treeNodes);

    while(workStack.Count > 0) {
        var current = workStack.Pop();
        if(current is Leaf)
            yield return current;
        else {
            for(n = 0; n < current.m_treeNodes.Count; n++) {
                workStack.Push(current.m_treeNodes[n]);
            }
        }
    }
}

Это должно выполнить ту же функцию без рекурсии.

Править: Daniel Plaisted отправил в комментарии о большей проблеме с рекурсивным вызовом Перечислителей, которым подводят итог в сообщении на Блогах MSDN относительно итераторов. Спасибо Daniel. =)

Другое Редактирование: Всегда помните, что простота кода может быть более важной, чем производительность, особенно если Вы ожидаете, что другие поддержат Ваш код. Если Вы не ожидаете, что Ваше дерево станет очень большим, используйте метод рекурсии lassevk обрисованный в общих чертах в его ответе.

3
ответ дан 13 December 2019 в 19:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: