zoukankan      html  css  js  c++  java
  • Rust Lang Book Ch.13 Iterators, Closures

    Closures

    Closure是匿名函数,并且可以存下来。此外,Closure会获取创建时的作用域中的变量。

    fn generate_workout(intensity: u32, random_number: u32) {
        let expensive_closure = |num| { //closure,以num为参数,返回num
            println!("calculating slowly...");
            thread::sleep(Duration::from_secs(2));
            num
        };
    
        if intensity < 25 {
            println!("Today, do {} pushups!", expensive_closure(intensity));
            println!("Next, do {} situps!", expensive_closure(intensity));
        } else {
            if random_number == 3 {
                println!("Take a break today! Remember to stay hydrated!");
            } else {
                println!(
                    "Today, run for {} minutes!",
                    expensive_closure(intensity)
                );
            }
        }
    }
    

      

    编译器会对Closure做一定程度的参数类型推理。

    fn  add_one_v1   (x: u32) -> u32 { x + 1 }
    let add_one_v2 = |x: u32| -> u32 { x + 1 };
    let add_one_v3 = |x|             { x + 1 };
    let add_one_v4 = |x|               x + 1  ;
    

      

    不过,一个Closure只能对应一套参数-返回值类型,编译器推理该Closure对应的类型之后,就会固定下来。

        let example_closure = |x| x;
    
        let s = example_closure(String::from("hello"));
        let n = example_closure(5);
                                ^
                                |
                                expected struct `std::string::String`, found integer
                                help: try using a conversion method: `5.to_string()`
    

      

    Closure至少实现了Fn, FnMut和FnOnce三种traits之中的一个。如果不需要创建Closure上下文中的变量,能传Closure的地方也能传Function

    impl<T> Cacher<T>
    where
        T: Fn(u32) -> u32,
    {
        fn new(calculation: T) -> Cacher<T> {
            Cacher {
                calculation,
                value: None,
            }
        }
    
        fn value(&mut self, arg: u32) -> u32 {
            match self.value {
                Some(v) => v,
                None => {
                    let v = (self.calculation)(arg);
                    self.value = Some(v);
                    v
                }
            }
        }
    }
    

      

    FnOnce直接consume(也即拿走ownership)它所需要的创建上下文中的变量,当然要注意由于ownership只能拿一次,所以FnOnce也只能调用一次。

    Fn则只读地从创建时的上下文中borrow values。

    FnMut从创建时的上下文中borrow values,同时还能改这些变量。

    fn main() {
        let x = 4;
    
        let equal_to_x = |z| z == x;//capture x
    
        let y = 4;
    
        assert!(equal_to_x(y));
    }
    

      

    Rust编译器会根据Closure内容决定该Closure实现哪些traits。所有Closure都实现了FnOnce。对于不会发生Move,也就是不会发生Ownership改动的Closure,编译器会令其实现FnMut。对哪些不会更改captured variables的值的Closures,编译器会令其实现Fn。

    如果一定要closure拿走它captured的variables的ownership,可以使用move关键字。

    fn main() {
        let x = vec![1, 2, 3];
    
        let equal_to_x = move |z| z == x;
    
        let y = vec![1, 2, 3];
    
        assert!(equal_to_x(y));
    }
    

      

    一般来说可以先试试用Fn来当trait bound,如果需要用FnMut或者FnOnce,编译器报错再改。

    Iterator

     Iterator是懒计算的,可以用来遍历集合。

        let v1 = vec![1, 2, 3];
    
        let v1_iter = v1.iter();
    
        for val in v1_iter {//这里不需要声明v1_iter为mutable,因为for循环自动帮我们标了
            println!("Got: {}", val);
        }
    

      

    所有的迭代器都实现了一个特性Iterator,Iterator实现了方法next(),返回一个包含子元素的Option,当迭代完成后,返回值为None,否则为Some(子元素)。

    pub trait Iterator {
        type Item;
    
        fn next(&mut self) -> Option<Self::Item>;
    
        // methods with default implementations elided
    }
    

      

        #[test]
        fn iterator_demonstration() {
            let v1 = vec![1, 2, 3];
    
            let mut v1_iter = v1.iter();
    
            assert_eq!(v1_iter.next(), Some(&1));
            assert_eq!(v1_iter.next(), Some(&2));
            assert_eq!(v1_iter.next(), Some(&3));
            assert_eq!(v1_iter.next(), None);
        }
    

      

    如果需要iterator接管被遍历的集合的ownership并返回带有ownership的值,可以使用into_iter。

    如果需要产生mutable reference,我们可以调用iter_mut。

    因为Iterator是懒加载的,所以可以把多个映射结合在一起形成计算链,最后再调用Consuming Adaptor得到最终结果。

        let v2: Vec<_> = v1.iter().map(|x| x + 1).collect()//这里map从v1.iter()生成了新的iterator
    

      

        shoes.into_iter().filter(|s| s.size == shoe_size).collect()
    

      

    Consuming Adaptors

    调用iterator的next()方法的函数被称为consuming adaptor

        #[test]
        fn iterator_sum() {
            let v1 = vec![1, 2, 3];
    
            let v1_iter = v1.iter();
    
            let total: i32 = v1_iter.sum();//sum是Consuming adaptor
    
            assert_eq!(total, 6);
        }
    

      

    Customed Iterator

    只要为自己的struct实现Iterator trait即可

    struct Counter {
        count: u32,
    }
    
    impl Counter {
        fn new() -> Counter {
            Counter { count: 0 }
        }
    }
    
    fn main() {}
    impl Iterator for Counter {
        type Item = u32;
    
        fn next(&mut self) -> Option<Self::Item> {
            if self.count < 5 {
                self.count += 1;
                Some(self.count)
            } else {
                None
            }
        }
    }
    

     

        #[test]
        fn using_other_iterator_trait_methods() {
            let sum: u32 = Counter::new()
                .zip(Counter::new().skip(1))//这里只会生成(1, 2), ...(4,5),这4对,(5, None)不会生成
                .map(|(a, b)| a * b)
                .filter(|x| x % 3 == 0)
                .sum();
            assert_eq!(18, sum);
        }
    

      

    用Iterator也可以读取std::env::args:

     

    impl Config {
        pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
            args.next();//第一个参数是程序本身,不要
    
            let query = match args.next() {
                Some(arg) => arg,
                None => return Err("Didn't get a query string"),
            };
    
            let filename = match args.next() {
                Some(arg) => arg,
                None => return Err("Didn't get a file name"),
            };
    
            let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
    
            Ok(Config {
                query,
                filename,
                case_sensitive,
            })
        }
    }
    

      

    Loops vs. Iterators 性能比较

    书中用for循环写了一个过滤文件语句找是否存在关键词的程序,又用iterator写了一版,关键代码分别是:

    for循环

    pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
        let mut results = Vec::new();
    
        for line in contents.lines() {
            if line.contains(query) {
                results.push(line);
            }
        }
    
        results
    }
    

      

    iterator:

    pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
        contents
            .lines()
            .filter(|line| line.contains(query))
            .collect()
    }
    

      

    用柯南道尔的福尔摩斯作为测试,二者性能差不多,甚至iterator更甚一筹。书中认为这是因为Iterator是zero-cost abstractions,即无代价的抽象。这些iterator实际编译的底端代码已经性能很优秀,所以不会付出更多的运行代价。而且使用iteration,在编译器已知循环次数很少的时候,还会自动启动unrolling,即直接生成重复性的代码,以节约loop控制时间。

  • 相关阅读:
    【nginx】常见的陷阱和错误
    linux配置文件的一些调优
    关于防刷票的一些问题
    关于nginx的限速模块
    使用Javascript监控前端相关数据
    【mysql】关于Index Condition Pushdown特性
    【mysql】关于临时表
    堆和栈的区别(转过无数次的文章)
    深入PHP内核之in_array
    【mysql】索引的优化
  • 原文地址:https://www.cnblogs.com/xuesu/p/13880731.html
Copyright © 2011-2022 走看看