试图通过循环替换尾递归调用

时间:2014-01-25 00:06:46

标签: c# tail-recursion

this问题上,有一个递归函数找到父:

public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
    DependencyObject parentObject = VisualTreeHelper.GetParent(child);
    if (parentObject == null) return null;
    var parent = parentObject as T;
    if (parent != null)
    {
        return parent;
    }
    return FindParent<T>(parentObject);
}

Resharper让我把它转换成循环而不是递归,就像那样:

public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
    while (true)
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);
        if (parentObject == null) return null;
        var parent = parentObject as T;
        if (parent != null)
        {
            return parent;
        }
        child = parentObject;
    }
}

现在我有一个功能可以折叠扩展组及其所有子组:

不幸的是,Reshaper不会像上面那样转换它:(

private void MenuItemCollapseAll_OnClick(object sender, RoutedEventArgs e)
{
    // ... truncated for brievety
    IEnumerable<CollectionViewGroup> groups = grid.Items.Groups.OfType<CollectionViewGroup>().ToArray();
    CollapseGroups(grid, groups);
}

public void CollapseGroups(DataGridControl grid, IEnumerable<CollectionViewGroup> groups)
{
    foreach (var @group in groups)
    {
        grid.CollapseGroup(group);
        var subGroups = @group.Items.OfType<CollectionViewGroup>().ToArray();
        if (subGroups.Any())
        {
            CollapseGroups(grid, subGroups);
        }
    }
}

我只是无法弄清楚如何使用while语句以相同的方式转换它,并不完全确定这个函数是否可能。

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

它不是尾递归的。递归调用之后是foreach循环的另一次迭代。但是,并非所有希望都失去了。

将它转换为迭代可以做的是创建一个Stack<Tuple<DataGridControl, IEnumerable<CollectionViewGroup>>并将新元组推入堆栈来替换递归调用。然后循环直到堆栈耗尽。

但首先,我注意到第一个参数永远不会改变。因此,迭代版本实际上并不需要将其保留在堆栈中:

public void CollapseGroups(DataGridControl grid, IEnumerable<CollectionViewGroup> groups)
{
    var to_process = new Stack<IEnumerable<CollectionViewGroup>>();
    to_process.Push(groups);
    do {
        groups = to_process.Pop();
        foreach (var @group in groups)
        {
            grid.CollapseGroup(@group);
            to_process.Push(@group.Items.OfType<CollectionViewGroup>());
        }
    } while (to_process.Count > 0);
}