【问题标题】:NonRecursive Equivalent of Lazy Recursive Functions惰性递归函数的非递归等价
【发布时间】:2015-11-17 21:44:12
【问题描述】:

考虑以下代码在玫瑰树上实现深度优先搜索:

public class RoseTree<T>
{
    public T Value;
    public IEnumerable<Lazy<RoseTree<T>> Children;

    public IEnumerable<T> Flatten()
    {
        yield return Value;
        foreach (var childValue in Children.SelectMany(t => t.Value.Flatten()))
            yield return childValue;
    }
}

我一直在努力想出一个等价的非递归等价物。特别是,虽然很容易得到一个严格的非递归、准等价的函数,例如:

public IEnumerable<T> FlattenIteratively()
{
    var roseTreeQueue = new Stack<RoseTree<T>>();
    var values = new Queue<T>();
    roseTreeQueue.Push(this);

    while (roseTreeQueue.Any())
    {
        var top = roseTreeQueue.Pop();
        values.Enqueue(top.Value);

        foreach (var child in top.Children.Reverse())
            roseTreeQueue.Push(child.Value);
    }

    return values;
} 

虽然对于具有定义值的有限树,这会产生与 Flatten 相同的结果,但对于无限树或具有未定义值的树,它会失败。有没有人看到编写一种非递归方法来遍历这个结构的方法,它具有与递归方法相同的特征。

注意:鉴于 C# 重写了 yield return 函数,调用第一个方法 recursive 有点误导。如果有人有更准确的术语,我很乐意提供。

【问题讨论】:

    标签: c# recursion tree


    【解决方案1】:

    一个通用的惰性迭代树遍历函数,取自How to flatten tree via LINQ?

    public static class TreeHelper
    {
        public static IEnumerable<T> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
        {
            var stack = new Stack<IEnumerator<T>>();
            var e = source.GetEnumerator();
            try
            {
                while (true)
                {
                    while (e.MoveNext())
                    {
                        var item = e.Current;
                        yield return item;
                        var elements = elementSelector(item);
                        if (elements == null) continue;
                        stack.Push(e);
                        e = elements.GetEnumerator();
                    }
                    if (stack.Count == 0) break;
                    e.Dispose();
                    e = stack.Pop();
                }
            }
            finally
            {
                e.Dispose();
                while (stack.Count != 0) stack.Pop().Dispose();
            }
        }
    }
    

    在你的情况下,它可以如下使用

    public class RoseTree<T>
    {
        public T Value;
        public IEnumerable<Lazy<RoseTree<T>>> Children;
        public IEnumerable<T> Flatten()
        {
            return Enumerable.Repeat(this, 1)
                .Expand(item => item.Children != null ? item.Children.Select(c => c.Value) : null)
                .Select(item => item.Value);
        }
    }
    

    【讨论】:

      【解决方案2】:

      我们可以通过将FlattenIterative 方法显式转换为IEnumerator&lt;T&gt; 来解决这个问题。我们定义:

      public class RoseTreeEnumerator<T> : IEnumerator<T>, IEnumerable<T>
      {
          public RoseTree<T> OriginalTree { get; private set; }
      
          private Stack<Lazy<RoseTree<T>>> TreeQueue { get; set; }
      
          private RoseTree<T> CurrentRoseTree { get; set; }
      
      
          public RoseTreeEnumerator(RoseTree<T> tree)
          {
              OriginalTree = tree;
              TreeQueue = new Stack<Lazy<RoseTree<T>>>();
              TreeQueue.Push(new Lazy<RoseTree<T>>(() => tree));
              CurrentRoseTree = null;
          }  
      
          #region IEnumerator<T> Members
      
          public T Current
          {
              get { return CurrentRoseTree.Value; }
          }
      
          public void Dispose()
          {
          }
      
          #endregion
      
          #region IEnumerator Members
      
          object System.Collections.IEnumerator.Current
          {
              get { return Current; }
          }
      
          public bool MoveNext()
          {
              if (TreeQueue.Any())
              {
                  CurrentRoseTree = TreeQueue.Pop().Value;
                  foreach (var tree in CurrentRoseTree.Children.Reverse())
                  {
                      TreeQueue.Push(tree);
                  }
                  return true;
              }
              else
              {
                  return false;
              }
          }
      
          public void Reset()
          {
              TreeQueue.Clear();
              CurrentRoseTree = null;
              TreeQueue.Push(new Lazy<RoseTree<T>>(() => OriginalTree));
          }
      
          #endregion
      
          #region IEnumerable<T> Members
      
          public IEnumerator<T> GetEnumerator()
          {
              return this;
          }
      
          System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
          {
              return this;
          }
          #endregion
      }
      

      然后可以定义

      public IEnumerable<T> FlattenPrime()
      {
          return new RoseTreeEnumerator<T>(this);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-07
        • 1970-01-01
        • 2017-06-18
        • 1970-01-01
        相关资源
        最近更新 更多