Recursion 如何构建一个迭代器来递归遍历文件树?

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_

我想一个接一个地懒洋洋地使用文件树的节点,同时在每个级别上对兄弟节点进行排序

在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_路径=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)),
        )
    }
}