• Rust使用Iterator优化grep


    Rust使用Iterator优化grep

    前言

    上次实现的grep功能基本OK,基本流程是: 读取命令行参数 --> 放到数组中 --> clone数组中的参数,用来新建Config类型实例。

    现在使用迭代器进行优化,Config::new直接接收一个迭代器。然后返回一个Config类型实例。

    还有search功能的实现,我们是新建一个mutable数组,然后将符合条件的&str插入数组中。最后返回这个数组。
    在函数式编程的思想中,我们要尽量减少mutable状态,这样使得代码更加清晰,而且有利于后面的并发增强。
    考虑使用Iterator Adaptor返回一个新的迭代器来实现。

    主要改动如下:

    1. Config::new()的改动
      接收参数变成了 Iterator trait
    impl Config {
        pub fn new(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {
            args.next();
            let query = match args.next() {
                Some(args) => args,
                None => return Err("Lack of query string"),
            };
            let filename = match args.next() {
                Some(args) => args,
                None => return Err("Lack of filename string"),
            };
            let ignore_case = env::var("IGNORE_CASE").is_ok();
            Ok(Config{query, filename, ignore_case})
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 相应的main()中也进行相应的改动
      参数不再是以前的&[String],变成了一个迭代器env::args()
    fn main() {
        let config = Config::new(env::args()).unwrap_or_else(|err| {
            eprintln!("Problem parse arguments: {}", err);
            process::exit(1);
        });
        //snip
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. search()的改动
      不再分配新的mut数组,而是直接通过Iterator Adaptor加collect()返回一个数组
    pub fn search<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
        content.lines()
        .filter(|x| x.contains(query)).collect()
    }
    
    • 1
    • 2
    • 3
    • 4

    同理:search_case_insentisive()也一样修改。

    最终代码如下

    main.rs

    use std::env;
    use std::process;
    use minigrep::Config;
    fn main() {
        let config = Config::new(env::args()).unwrap_or_else(|err| {
            eprintln!("Problem parse arguments: {}", err);
            process::exit(1);
        });
        println!("query: {}, filename: {}", config.query, config.filename);
        
        if let Err(e) = minigrep::run(config) {
            eprintln!("Application Error: {}", e);
            process::exit(1);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    lib.rs

    use std::error::Error;
    use std::fs;
    use std::env;
    pub struct Config {
        pub query: String,
        pub filename: String,
        pub ignore_case: bool,
    }
    impl Config {
        pub fn new(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {
            args.next();
            let query = match args.next() {
                Some(args) => args,
                None => return Err("Lack of query string"),
            };
            let filename = match args.next() {
                Some(args) => args,
                None => return Err("Lack of filename string"),
            };
            let ignore_case = env::var("IGNORE_CASE").is_ok();
            Ok(Config{query, filename, ignore_case})
        }
    }
    pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
        let content = fs::read_to_string(config.filename)?;
        // println!("With text:\n{}", content);
        let result = if config.ignore_case {
            search_case_insensitive(&config.query, &content)
        } else {
            search(&config.query, &content)
        };
            for line in result {
                println!("{}", line);
        }
        Ok(())
    }
    
    pub fn search<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
        content.lines()
        .filter(|x| x.contains(query)).collect()
    }
    
    pub fn search_case_insensitive<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
        let query = query.to_lowercase();
        content.lines()
        .filter(|x| {
            x.to_lowercase().contains(&query)
        }).collect()
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn one_result() {
            let query = "xwp";
            let content = "\
    Hello,rust!
    xwp is handsome
    You know!";
            assert_eq!(vec!["xwp is handsome"], search(query, content));
        }
    
        #[test]
        fn case_insensitive() {
            let query = "xWp";
            let content = "\
    Hello,rust!
    xwp is handsome
    You KonW";
            assert_eq!(vec!["xwp is handsome"], search_case_insensitive(query, content));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
  • 相关阅读:
    springboot2.0 读取yml 配置 array,list,map,单值,及其组合
    基于文本提示的图像目标检测与分割实践
    android: Preferences DataStore 和 Proto DataStore use guide
    Java---SSM---SpringMVC(2)
    How to Set Profiles for Spring
    MySQL 中的 CASE WHEN 和功能类似的方法
    【c++】cpp类和对象
    协议-TCP协议-基础概念03-Keep live保活机制-TCP RST-TCP连接
    MySQL-数据备份与还原
    2019 国际大学生程序设计竞赛(ICPC)亚洲区域赛(银川) 7题
  • 原文地址:https://blog.csdn.net/qq_46480020/article/details/126072211