我已经创建了一个模板化的bst类,并且我已经实现了GetEnumerator,但我对所做的事情感到非常满意。
所以首先我觉得我需要帮助函数内部访问
private IEnumerable<Node<T>> innerVisit(Node<T> root)
{
if(root== null)
yield break;
if (root.Left != null)
{
var l = innerVisit(root.Left);
foreach (var item in l)
yield return item;
}
yield return root;
if (root.Right != null)
{
var r = innerVisit(root.Right);
foreach (var item in r)
yield return item;
}
}
我真的不喜欢重复的代码,但找不到合适的解决方案 ,显然重复循环是不洁净的,但我觉得将它埋在一个可以跳入这个功能的功能中至少可以说是不好的做法。关于如何正确地做到这一点的任何建议?
也完成了我写的实现
public IEnumerator<Node<T>> GetEnumerator()
{
var res = innerVisit(_root);
foreach (var item in res)
yield return item;
}
但是这也太糟糕了,而且确保它能在foreach循环中运行等等。
答案 0 :(得分:2)
我不认为你可以解决你的问题,而不会像你一样重复相同的操作,但我认为你可以更清楚地做到这一点,这是返回IEumerable的要求的一部分,如果你我希望以递归的方式做到这一点,你不能阻止收益操作(但你不必自己编写所有这些,System.LINQ会帮助我们)。
我们可以用Enumerable.Concat方法替换foreach和yield返回,使用它我们可以连接我们将为节点本身创建的左InnerVisit IEnumerable,IEnumerable(一个当前节点有1个项目的数组)和右边InnerVisit IEnumerable:
private IEnumerable<Node<T>> InnerVisit(Node<T> node)
{
if(node == null)
{
return Enumerable.Empty<Node<T>>;
}
return InnerVisit(node.Left).Concat(new[] { node }).Concat(InnerVisit(node.Right));
}
请注意,在调用递归方法之前无需检查Left或Right是否为null,因为它将在内部调用中稍后检查它,如果它为null,我们将返回Enumerable.Empty<Node<T>>
而不是使用像你一样让收益突破。
我们还可以通过直接在根InnerVisit的结果上调用GetEnumerator来简化GetEnumerator,如下所示:
public IEnumerator<Node<T>> GetEnumerator()
{
return InnerVisit(_root).GetEnumerator();
}