Recursion 如何构建一个迭代器来递归遍历文件树?
我想一个接一个地懒洋洋地使用文件树的节点,同时在每个级别上对兄弟节点进行排序 在Python中,我将使用同步生成器:Recursion 如何构建一个迭代器来递归遍历文件树?,recursion,rust,tree,iterator,Recursion,Rust,Tree,Iterator,我想一个接一个地懒洋洋地使用文件树的节点,同时在每个级别上对兄弟节点进行排序 在Python中,我将使用同步生成器: def traverse_dst(src_dir、dst_root、dst_step): """ 递归地遍历源目录并生成(src,dst)对序列; """ dirs,files=list_dir_groom(src_dir)#获得直接后代。 对于目录中的d: 步骤=列表(dst_步骤) 步骤.附加(d.name) 从横向(d,dst根,阶跃)得到的产量 对于文件中的f: dst_
def traverse_dst(src_dir、dst_root、dst_step):
"""
递归地遍历源目录并生成(src,dst)对序列;
"""
dirs,files=list_dir_groom(src_dir)#获得直接后代。
对于目录中的d:
步骤=列表(dst_步骤)
步骤.附加(d.name)
从横向(d,dst根,阶跃)得到的产量
对于文件中的f:
dst_路径=dst_根.joinpath(步骤)
屈服f,dst_路径
在Elixir中,一种(惰性)流:
我们可以看到一些处理递归的语言特性:yield from
在Python中,flat\u map
在Elixir中;后者看起来像是一种经典的功能性方法
它看起来像是Rust中懒惰的东西,它总是一个迭代器。我该如何在锈迹中或多或少地做相同的事情呢
我想保留递归函数的结构,使用dirs
和文件
作为路径向量(可以选择对它们进行排序和过滤)
获取目录
和文件
已经按照我的喜好实现了:
fn folders(dir: &Path, folder: bool) -> Result<Vec<PathBuf>, io::Error> {
Ok(fs::read_dir(dir)?
.into_iter()
.filter(|r| r.is_ok())
.map(|r| r.unwrap().path())
.filter(|r| if folder { r.is_dir() } else { !r.is_dir() })
.collect())
}
fn list_dir_groom(dir: &Path) -> (Vec<PathBuf>, Vec<PathBuf>) {
let mut dirs = folders(dir, true).unwrap();
let mut files = folders(dir, false).unwrap();
if flag("x") {
dirs.sort_unstable();
files.sort_unstable();
} else {
sort_path_slice(&mut dirs);
sort_path_slice(&mut files);
}
if flag("r") {
dirs.reverse();
files.reverse();
}
(dirs, files)
}
我想要的(还没有工作!):
fn横向平面测试仪(src_dir:&PathBuf,dst_step:Vec){
let(dirs,files)=list\u dir\u groom(src\u dir);
让横移=| d |{
让mut step=dst_step.clone();
push(PathBuf::from(d.file_name().unwrap());
横向平直测试仪(d,步进);
};
//这是我只希望是真的!
平面地图(方向、导线)+地图(文件)
}
我希望这个函数能够按照长生不老药解决方案的精神,提供一个长而平的文件迭代器。我只是还不能处理必要的返回类型和其他语法。我真的希望这次能说清楚
我设法编译和运行的内容(毫无意义,但签名是我真正想要的):
fn横向平面测试仪(
src_dir:&PathBuf,
dst_步骤:Vec,
)->impl迭代器{
let(dirs,files)=list\u dir\u groom(src\u dir);
让| u遍历=| d:&PathBuf |{
让mut step=dst_step.clone();
push(PathBuf::from(d.file_name().unwrap());
横向平坦测试仪(d,步进)
};
.into|iter().map(|f |(f,PathBuf::new())
}
我仍然缺少的是:
fn traverse_flat_dst_iter(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> impl Iterator<Item = (PathBuf, PathBuf)> {
let (dirs, files) = list_dir_groom(src_dir);
let traverse = |d: &PathBuf| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_flat_dst_iter(d, step)
};
// Here is a combination amounting to an iterator,
// which delivers a (PathBuf, PathBuf) tuple on each step.
// Flat mapping with traverse, of course (see Elixir solution).
// Iterator must be as long as the number of files in the tree.
// The lines below look very close, but every possible type is mismatched :(
dirs.into_iter().flat_map(traverse)
.chain(files.into_iter().map(|f| (f, PathBuf::new())))
}
fn横向平面测试仪(
src_dir:&PathBuf,
dst_步骤:Vec,
)->impl迭代器{
let(dirs,files)=list\u dir\u groom(src\u dir);
让遍历=| d:&PathBuf |{
让mut step=dst_step.clone();
push(PathBuf::from(d.file_name().unwrap());
横向平坦测试仪(d,步进)
};
//这是一个迭代器的组合,
//它在每个步骤上提供一个(PathBuf,PathBuf)元组。
//当然,使用导线进行平面映射(请参见Elixir解决方案)。
//迭代器的长度必须与树中的文件数相同。
//下面的几行看起来非常接近,但每种可能的类型都不匹配:(
直达平面图(导线测量)
.chain(files.into_iter().map(|f |(f,PathBuf::new()))
}
有两种方法:
第一种是使用现有的板条箱,比如。好处是它经过了很好的测试,提供了很多选择
第二个是编写自己的迭代器实现。下面是一个示例,可能是您自己实现迭代器的基础:
结构文件迭代器{
dirs:Vec,//等待读取的dir
files:Option,//当前读取目录上的非递归迭代器
}
文件迭代器的impl From{
fn from(路径:&str)->Self{
文件迭代器{
dirs:vec![PathBuf::from(path)],
档案:无,
}
}
}
文件迭代器的impl迭代器{
类型项=PathBuf;
fn下一步(&mut self)->选项{
环路{
而让一些(read_dir)=&mut self.file{
匹配read_dir.next(){
一些(确定(条目))=>{
让path=entry.path();
如果让Ok(md)=entry.metadata(){
如果md.is_dir(){
self.dirs.push(path.clone());
继续;
}
}
返回一些(路径);
}
None=>{//我们使用了这个目录
self.files=无;
打破
}
_ => { }
}
}
而让Some(dir)=self.dirs.pop(){
让read_dir=fs::read_dir(&dir);
如果让Ok(文件)=读取目录{
self.files=一些(文件);
返回一些(dir);
}
}
break;//不再有文件,不再有目录
}
不返回任何值;
}
}
编写自己的迭代器的好处是,您可以根据自己的精确需要(排序、筛选、错误处理等)对其进行调整。但您必须处理自己的错误。有两种方法: 第一种是使用现有的板条箱,比如。好处是它经过了很好的测试,提供了很多选择 第二个是编写自己的迭代器实现。下面是一个示例,可能是您自己实现迭代器的基础: 结构文件迭代器{ dirs:Vec,//等待读取的dir files:Option,//当前读取目录上的非递归迭代器 } 文件迭代器的impl From{ fn from(路径:&str)->Self{ 文件迭代器{ dirs:vec![PathBuf::from(path)], 档案:无, } } } 文件迭代器的impl迭代器{ 类型项=PathBuf; fn下一步(&mut self)->选项{ 环路{ 而让一些(read_dir)=&mut
fn traverse_flat_dst(src_dir: &PathBuf, dst_step: Vec<PathBuf>) {
let (dirs, files) = list_dir_groom(src_dir);
for d in dirs.iter() {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
println!("d: {:?}; step: {:?}", d, step);
traverse_flat_dst(d, step);
}
for f in files.iter() {
println!("f: {:?}", f);
}
}
fn traverse_flat_dst_iter(src_dir: &PathBuf, dst_step: Vec<PathBuf>) {
let (dirs, files) = list_dir_groom(src_dir);
let traverse = |d| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_flat_dst_iter(d, step);
};
// This is something that I just wish to be true!
flat_map(dirs, traverse) + map(files)
}
fn traverse_flat_dst_iter(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> impl Iterator<Item = (PathBuf, PathBuf)> {
let (dirs, files) = list_dir_groom(src_dir);
let _traverse = |d: &PathBuf| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_flat_dst_iter(d, step)
};
files.into_iter().map(|f| (f, PathBuf::new()))
}
fn traverse_flat_dst_iter(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> impl Iterator<Item = (PathBuf, PathBuf)> {
let (dirs, files) = list_dir_groom(src_dir);
let traverse = |d: &PathBuf| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_flat_dst_iter(d, step)
};
// Here is a combination amounting to an iterator,
// which delivers a (PathBuf, PathBuf) tuple on each step.
// Flat mapping with traverse, of course (see Elixir solution).
// Iterator must be as long as the number of files in the tree.
// The lines below look very close, but every possible type is mismatched :(
dirs.into_iter().flat_map(traverse)
.chain(files.into_iter().map(|f| (f, PathBuf::new())))
}
fn traverse_flat_dst_iter(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> impl Iterator<Item = (PathBuf, PathBuf)> {
let (dirs, files) = list_dir_groom(src_dir);
let traverse = move |d: PathBuf| -> Box<dyn Iterator<Item = (PathBuf, PathBuf)>> {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
Box::new(traverse_flat_dst_iter(&d, step))
};
dirs.into_iter()
.flat_map(traverse)
.chain(files.into_iter().map(|f| (f, PathBuf::new())))
}
fn traverse_dir(
src_dir: &PathBuf,
dst_step: Vec<PathBuf>,
) -> Box<dyn Iterator<Item = (PathBuf, Vec<PathBuf>)>> {
let (dirs, files) = groom(src_dir);
let destination_step = dst_step.clone(); // A clone for handle.
let traverse = move |d: PathBuf| {
let mut step = dst_step.clone();
step.push(PathBuf::from(d.file_name().unwrap()));
traverse_dir(&d, step)
};
let handle = move |f: PathBuf| (f, destination_step.clone());
if flag("r") {
// Chaining backwards.
Box::new(
files
.into_iter()
.map(handle)
.chain(dirs.into_iter().flat_map(traverse)),
)
} else {
Box::new(
dirs.into_iter()
.flat_map(traverse)
.chain(files.into_iter().map(handle)),
)
}
}