在C#中编写F#递归文件夹访问者 – seq vs IEnumerable
我经常在F#中使用这个递归的“访客”
let rec visitor dir filter= seq { yield! Directory.GetFiles(dir, filter) for subdir in Directory.GetDirectories(dir) do yield! visitor subdir filter}
最近我开始在C#中实现一些F#function,我正在尝试将其重现为IEnumerable,但是我遇到的困难比这更进一步:
static IEnumerable Visitor(string root, string filter) { foreach (var file in Directory.GetFiles(root, filter)) yield return file; foreach (var subdir in Directory.GetDirectories(root)) foreach (var file in Visitor(subdir, filter)) yield return file; }
我不明白为什么我必须在C#版本中为递归做一个双重foreach,而不是在F#中… seq {}是否隐含地执行’concat’?
yield!
执行’flatten’操作,因此它将您传递给外部序列的序列集成在一起,隐式地对序列的每个元素执行foreach
在每个元素上yield
。
没有简单的方法可以做到这一点。 您可以通过定义可以存储一个值或一系列值的C#类型来解决此问题 – 使用F#表示法:
type EnumerationResult<'a> = | One of 'a | Seq of seq<'a>
(以你喜欢的任何方式将其转换为C#:-))
现在,你可以这样写:
static IEnumerable> Visitor (string root, string filter) { foreach (var file in Directory.GetFiles(root, filter)) yield return EnumerationResult.One(file); foreach (var subdir in Directory.GetDirectories(root)) yield return EnumerationResult.Seq(Visitor(subdir, filter)) } }
要使用它,你必须编写一个扁平化EnumerationResult的函数,它可以是C#中的扩展方法,具有以下签名:
IEnumerable Flatten(this IEnumerable> res);
现在,这是一个棘手的部分 – 如果你以一种直接的方式实现它,它仍然包含“forach”迭代嵌套的“Seq”结果。 但是,我相信您可以编写一个不具有二次复杂度的优化版本。
好吧..我想这是一个博客文章的主题,而不是可以在这里完整描述的东西:-),但希望,它显示了一个想法,你可以尝试跟随!
[编辑:当然,你也可以使用“Flatten”的天真实现,使用“SelectMany”只是为了使你的C#迭代器代码的语法更好]
在检索特定目录下的所有文件的特定情况下, Directory.GetFiles
这种重载效果最好:
static IEnumerable Visitor( string root, string filter ) { return Directory.GetFiles( root, filter, SearchOption.AllDirectories ); }
在遍历可枚举对象树的一般情况下,需要嵌套的foreach循环或等效项(另请参阅: All About Iterators )。
编辑:添加了一个函数示例,将任何树展平为枚举:
static IEnumerable Flatten ( T item, Func> next ) { yield return item; foreach( T child in next( item ) ) foreach( T flattenedChild in Flatten( child, next ) ) yield return flattenedChild; }
这可用于选择所有嵌套文件,如前所述:
static IEnumerable Visitor( string root, string filter ) { return Flatten( root, dir => Directory.GetDirectories( dir ) ) .SelectMany( dir => Directory.GetFiles( dir, filter ) ); }
在C#中,我使用以下代码来实现这种function:
public static IEnumerable TryGetDirectories(this DirectoryInfo dir) { return F.Swallow(() => dir.GetDirectories(), () => new DirectoryInfo[] { }); } public static IEnumerable DescendantDirs(this DirectoryInfo dir) { return Enumerable.Repeat(dir, 1).Concat( from kid in dir.TryGetDirectories() where (kid.Attributes & FileAttributes.ReparsePoint) == 0 from desc in kid.DescendantDirs() select desc); }
这解决了IO错误(不幸的是,它不可避免地发生),并且避免了由于符号链接而导致的无限循环(特别是,您将遇到在Windows 7中搜索某些目录)。
上述就是C#学习教程:在C#中编写F#递归文件夹访问者 – seq vs IEnumerable分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/997193.html