Enumerating Directories iteratively in “postorder”

一笑奈何 提交于 2019-12-11 23:19:37

问题


Suppose I wanted to enumerate through a given directory's files & directories iteratively, in such a way that the inner directories and files are given out first.

If you were to execute this enumerable in a foreach loop with the functions: DeleteFile and DeleteEmptyDirectory it should not fail, because the most inner items are yielded out first.

Now of course one could do this (pseudocode):

func(Directory dir)
    foreach (var f in dir.EnumerateFileSystemInfos())
        if (f is FileInfo)
            yield return f;
        else
            foreach (var item in func(f))
                yield return item;

    yield return dir;

But that has wastes a lot of allocations on enumerators.

One could also use two stacks and "acquire" all of the directories together and then just keep popping out items but that wastes too much memory and doesn't balance the MoveNext times that well.

Any ideas on how to do this efficiently without wasting too much space?


回答1:


In the spirit of DRY principle, I would use the function from my answer to How to make an IEnumerable of values of tree nodes?

public static class TreeHelper
{
    public static IEnumerable<T> Traverse<T>(T node, Func<T, IEnumerable<T>> childrenSelector, bool preOrder = true)
    {
        var stack = new Stack<IEnumerator<T>>();
        var e = Enumerable.Repeat(node, 1).GetEnumerator();
        try
        {
            while (true)
            {
                while (e.MoveNext())
                {
                    var item = e.Current;
                    var children = childrenSelector(item);
                    if (children == null)
                        yield return item;
                    else
                    {
                        if (preOrder) yield return item;
                        stack.Push(e);
                        e = children.GetEnumerator();
                    }
                }
                if (stack.Count == 0) break;
                e.Dispose();
                e = stack.Pop();
                if (!preOrder) yield return e.Current;
            }
        }
        finally
        {
            e.Dispose();
            while (stack.Count != 0) stack.Pop().Dispose();
        }
    }
}

Utilizing it for your case would be something like this

var directory = new DirectoryInfo(path);
var result = TreeHelper.Traverse<FileSystemInfo>(directory, fsi => 
    fsi is DirectoryInfo ? ((DirectoryInfo)fsi).EnumerateFileSystemInfos() : null,
    preOrder: false)


来源:https://stackoverflow.com/questions/34440512/enumerating-directories-iteratively-in-postorder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!