Rust基础学习笔记(四):闭包与迭代器

本章主要记录Rust中闭包与迭代器的用法,并用其改善现有代码以提升性能。所有代码来自官方文档。

闭包

闭包是保存在变量里、作为参数传递给其他函数的匿名函数。其特点是能够捕获其所在定义区间内的变量值。

定义

let value = |para1,para2|{ ... };

 

 Value是闭包赋予的变量名,||中间是所需的参数(多个参数由逗号分隔)

使用

println!("Today, do {} pushups!", expensive_closure(intensity));

类型

闭包不必要声明其参数和返回值的类型,只有一行代码时甚至可以省略大括号:

let expensive_closure = |num: u32| -> u32 x+1;

 

而一旦被使用,闭包的参数与返回值的类型便会确定,之后若出现不匹配则会报错:

let example_closure = |x| x;

let s = example_closure(String::from("hello"));
let n = example_closure(5);    //在这一行会报错

 

通过泛型参数和Fn特性存储闭包

通过这个特性可以保留闭包的计算结果避免重复调用或冗余代码,保存在一个结构体中。

这个结构体使用一个实现了Fn、FnOnce或者FnMut的泛型,并需要指定闭包参数、返回值的结构:

struct Cather<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}

 

可以看出,如果闭包不传递参数的话,也许最好运用函数来替代。

然后要设置一个保存其值的机制:

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
                }
        }
    }
}

 

然后在新建闭包时用Cacher::new方法,使用闭包时用Cacher::value方法。

这种方法存在的限制

显然这种方法默认每次调用闭包时参数都是一致的。可以改造结构体,使其加载一个哈希表来解决这一问题。

这种方法还只允许使用一个泛型,不过可以手动增加。

捕获定义域中的变量

闭包可以捕获其所在定义域中的变量,函数则不能:

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

 

编译可以通过。本质上闭包通过将环境中的变量保存起来来使用的,这种开销不被函数允许。

同样闭包可以以三种(所有权、动态引用、静态引用)方式获取环境,也对应三种(FnOnce/FnMut/Fn)Fn特性。编译器会根据具体情况实现这些特性。

也可以通过一些手段强制应用其中的一些特性,例如move能够将环境中的变量强制转移所有权:

let equal_to_x = move |z| z == x;

 

可以利用这种特性将所有权移交给其他线程。

迭代器

生成迭代器:

let v1 = vec![1, 2, 3];

let v1_iter = v1.iter();

 这个迭代器现在还不会完成任何工作。

使用迭代器:

for val in v1_iter {
    println!("Got: {}", val);
}

迭代器特性及next方法

迭代器特性只需要实现一个next方法。next方法顾名思义指向下一个元素(包装在Some(&)里),没有返回None。

事实上不断调用next方法的过程即耗尽迭代器的过程。

iter迭代器返回静态引用,into_iter可以移交所有权,iter_mut返回动态引用。

消耗迭代器的方法

指sum方法,它会取用迭代器的所有权来求和。求和之后便不能再使用该迭代器。

#[test]
fn iterator_sum() {
    let v1 = vec![1, 2, 3];
    let v1_iter = v1.iter();
    let total: i32 = v1_iter.sum();
    //v1_iter无了
    assert_eq!( total, 6); //通过编译
}

 

产生迭代器的方法

指map方法,通过闭包建立一一映射。

let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x+1).collect();
assert_eq!(v2, vec![2, 3, 4]);

使用闭包来捕获环境中的变量

指filter方法,逐个比对迭代器中的值,每个根据闭包内的运算结果返回布尔值:

#[derive(PartialEq, Debug)]
struct Shoe {
    size: u32,
    style: String,
}

fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
    shoes.into_iter().filter(|s| s.size == shoe_size).collect()
//注意这里用了into_iter }
#[cfg(test)] mod tests { use super::*; #[test] fn filters_by_size() { let shoes = vec![ Shoe { size: 10, style: String::from("sneaker"), }, Shoe { size: 13, style: String::from("sandal"), }, Shoe { size: 10, style: String::from("boot"), }, ]; //环境 let in_my_size = shoes_in_my_size(shoes, 10); //过滤 assert_eq!( in_my_size, vec![ Shoe { size: 10, style: String::from("sneaker") }, Shoe { size: 10, style: String::from("boot") }, ] ); } } fn main() {}

 

通过编译

通过迭代器特性实现自己的迭代器

声明->实现即可

struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

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))
            .map(|(a, b)| a * b)
            .filter(|x| x % 3 == 0)
            .sum();
        assert_eq!(18, sum);
    }

 

通过迭代器改进程序

改的是上一章的程序,故略

效率

迭代器能在提供更高级抽象的同时不消耗多余时间。

 

posted @ 2020-08-08 13:01  风坞  阅读(402)  评论(0)    收藏  举报