【问题标题】:Recursion in C# iteratorC#迭代器中的递归
【发布时间】:2011-05-11 06:23:21
【问题描述】:

是否可以在实现System.Collections.IEnumerable 的迭代器中使用递归?我有一个大致如下声明的树结构:

public class Node
{
    public Node Sibling;
    public Node Child;
}

我想遍历树中的节点。我想做这样的事情(伪代码,我猜这不会编译):

public class NodeIterator : System.Collections.IEnumerable
{
    Node m_root;

    public System.Collections.IEnumerator GetEnumerator()
    {
        recursiveYield(m_root);
    }

    System.Collections.IEnumeraton recursiveYield(Node node)
    {
        yield return node;
        if (node.Child)
        {
            recursiveYield(node.Child);
        }
        if (node.Sibling)
        {
            recursiveYield(node.Sibling);
        }
    }
}

这有可能吗?我意识到这可以在GetEnumerator 函数中使用Node deque 来解决,而无需递归。

【问题讨论】:

  • 应该可以。你试过了吗?客户不应该关心它是如何实现的——它只需要能够遍历接收器。如果递归使您的代码更简单,请使用它。
  • @Gishu - 应该可以工作(除了IEnumeraton),但它忽略了recursiveYield(node.Child)recursiveYield(node.Sibling) 的返回值。
  • 不完全是重复的,但C# Performance of nested yield in a tree 展示了一些解决方案并讨论了一些性能影响。

标签: c# iterator


【解决方案1】:

是的,您只需要迭代调用站点的返回值。像这样:

IEnumerable<T> Recursive(Node node)
{
    yield return node;
    foreach (var siblingNode in Recursive(node.Sibling))
    {
        yield return siblingNode;
    }
    foreach (var childNode in Recursive(node.Child))
    {
        yield return childNode;
    }
}

为了记录,这并不比使用队列来实现例如更好。广度优先遍历。在最坏的情况下,此类东西的内存要求是相同的。

【讨论】:

  • 我是否需要创建一个迭代器,或者是否可以创建一个递归产生不同值的静态函数:static IEnumberable&lt;T&gt; Recursive(Node node),然后在客户端foreach(var node in Recursive(root))
  • 嗨 John Leidegren,是的,这很漂亮 - 简短而甜蜜:) 但是你确定内存要求是相同的吗?如果我记得正确检查了类似的代码,那根本不是!每次调用都会创建新的 ArrayList 或 List
  • @Andreas Brinck 上下文关键字yield 用于告诉编译器此方法的返回值是一系列值IEnumerable&lt;T&gt;,而不是单个值。这就是为什么以及如何工作。如果你自己实现遍历,你最终会得到三倍的代码。如果您问是否不需要自己实现 IEnumerable/IEnumerator,那么这是真的。这可以是客户端可以按照建议使用的静态方法。
  • 我可以补充一点,在 F# 中,您可以使用 yield 更自然地做到这一点!运算符(使您免于迭代递归调用的结果)。
  • 我需要让自己用 F# 函数式编写更多代码从来都不是无聊的时刻。
【解决方案2】:

不可以,因为recursiveYield(Node node) 函数会返回一个集合,而您只能生成一个项目

【讨论】:

  • 虽然你不能将return collectionyield return item 结合起来,但有一个简单的解决方法。
  • 好吧,您通常应该更喜欢工作解决方法:)
  • 这是我通常在工作中使用的,但我希望明天是我做对的那一天:) 每一天我都希望......也许明天:)
  • 现在@John Leidegren 代码可以工作,我也写过类似的代码,但性能并不好!您在每次调用时创建一个 List 或 ArrayList (如果我没记错的话,没有反汇编程序)!但是您已经准备好对一件商品感兴趣了。这不应该看到部署!当我查看 IL 时,我更改了类似的代码。虽然它很短 * 很漂亮,但这不是解决这个问题的正确方法。
  • @ivo s,它并没有真正返回一个集合。它返回一个枚举器。不同之处在于集合包含它的值,但枚举器动态生成值。 Enumerator 只能枚举一次(无需创建新的 Enumerator),而集合允许重复枚举,并且可能允许通过索引或键访问。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-05
  • 2020-08-17
  • 2016-07-21
  • 1970-01-01
  • 2012-10-22
  • 2019-06-20
  • 2012-11-06
相关资源
最近更新 更多