Rust(13章闭包)

一 闭包

1.使用闭包创建抽象行为

	闭包: 可以捕获其所在环境的匿名函数
	闭包:
	- 是匿名函数
	- 保存为变量、作为参数
	- 可在一个地方创建闭包,然后在另一个上下文中调用闭包来完成运算
	- 可从定义的作用域捕获值
	
	例: 生成自定义运动计划的程序
	算法的逻辑不是重点,重点是算法中的计算过程需要几秒钟时间
	目标: 不让用户发生不必要的等待
	- 仅在必要时调用该算法
	- 只调用一次
use std::{time::Duration, thread};

fn main() {
    println!("Hello, world!");
}

//模拟复杂的算法
fn sim_ex_ca(intensity: u32) -> u32 {
    println!("ca slowly ....");
    thread::sleep(Duration::from_secs(2));  // 睡了2sec
    intensity
}

fn generate_workout(intensity: u32, random_number: u32) {
    if intensity < 25 {
        println!(
            "today, do {} pushups",
            sim_ex_ca(intensity)
        );
        println!(
            "next, do {} pushups",
            sim_ex_ca(intensity)
        );
    } else {
        if random_number == 3 {
            println!("take a break today!");
        } else {
            println!("today, run for {} klims",
                sim_ex_ca(intensity)
            );
        }
    }
}

其中在 generate_workout 函数中,多次调用 模拟算法,比较费时 , 进行该改善:

fn generate_workout(intensity: u32, random_number: u32) {
    let expensive_result = sim_ex_ca(intensity);
    if intensity < 25 {
        println!("today, do {} pushups",expensive_result);
        println!("next, do {} pushups",expensive_result);
    } else {
        if random_number == 3 {
            println!("take a break today!");
        } else {
            println!("today, run for {} klims",expensive_result);
        }
    }
}

此时虽然对只对 模拟算法 只调用了一次,相比第一次的代码的已经有了较好的改善,但是本次代码中出现的问题是:对该模拟算法 总是会调用一次

再次进行改善:
在其中使用了闭包

但是还没有达到上面实现的目标

fn generate_workout(intensity: u32, random_number: u32) {
    
    //定义的是闭包
    let expensive_closure = |num| {
        println!(" slowing");
        thread::sleep(Duration::from_secs(2));
        num
    };
    
    if intensity < 25 {
        println!("today, do {} pushups",expensive_closure(intensity));
        println!("next, do {} pushups",expensive_closure(intensity));
    } else {
        if random_number == 3 {
            println!("take a break today!");
        } else {
            println!("today, run for {} klims",expensive_closure(intensity));
        }
    }
}

2. 闭包类型推断和标注

	闭包不要求标注参数和返回值的类型
	函数是暴露给用户,要显示接口的一部分,所以会指定类型
	闭包通常很短小,只在狭小的上下文中工作,编译器通常能推断出类型

比较函数和闭包的定义语法

		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;

闭包的定义最终只会为参数/返回值推断出唯一具体的类型

fn main() {
    println!("Hello, world!");

    let example_closure = |x| x;
    let s = example_closure(String::from("hello"));

    let n = example_closure(5);
}

3. 使用泛型参数和Fn Trait来存储闭包

继续解决 运动计划 点击跳转遗留的问题

		给出一个解决方案:
		创建一个struct ,它持有闭包以及调用结果
	 	- 只会在需要结果时才执行该闭包
		 -  可缓存结果
		这个模式通常叫做记忆化 或 延迟计算

如何让struct持有闭包

		struct的定义需要知道所有字段的类型
		 - 需要指明闭包的类型
		 每个闭包实例都有自己唯一的匿名类型,即使两个闭包签名完全一样
		 所以需要使用:  **泛型和Trait Bound**
		 Fn Trait
		 Fn traits 由标准库提供
		 所有的闭包都至少实现以下trait 之一:
		 - Fn
		 - FnMut
		 - FnOnce
use std::{time::Duration, thread};

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


fn main() {
   let value = 10;
   let number = 7;

   generate_workout(value, number);
}



//模拟复杂的算法

fn generate_workout(intensity: u32, random_number: u32) {
    
    //定义的是一个匿名函数
    let mut expensive_closure =Cacher::new( |num| {
        println!(" slowing");
        thread::sleep(Duration::from_secs(2));
        num
    });
    
    if intensity < 25 {
        println!("today, do {} pushups",expensive_closure.value(intensity));
        println!("next, do {} pushups",expensive_closure.value(intensity));
    } else {
        if random_number == 3 {
            println!("take a break today!");
        } else {
            println!("today, run for {} klims",expensive_closure.value(intensity));
        }
    }
}

使用缓存器(Cacher)实现的限制

1. Cacher实例假定针对不同的参数arg,value方法总会得到相同的值
解决方法:
可以使用Hashmap替代单个值
 key:arg参数
 value:  执行闭包的结果
2. 只能接受一个u32类型的参数和u32类型的返回值

4.使用闭包捕获环境

闭包可以访问定义它的作用域内的变量,而普通函数则不能
闭包从所在环境捕获值的方式

        与函数获得参数的三种方式一样:
        1. 取得所有权: FnOnce
        2. 可变借用: FnMut
        3. 不可变借用: Fn
        - 所有的闭包都实现了FnOnce
        - 没有移动捕获变量的实现了FnMut
        - 无需可变访问捕获变量的闭包实现了

二 迭代器

迭代器模式: 对一系列项执行某些任务
迭代器负责:
				-遍历每个项
				-确定序列(遍历)何时完成
Rust的迭代器:
--惰性的: 除非调用消费迭代器的方法,否则迭代器本身没有任何效果

1.Iterator trait 和next方法

所有迭代器都实现了iterator trait
Iterator trait 定义于标准库, 定义大致如下:

pub trait Iterator  {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;

    // type Item 和self::Item 定义了与此该trait关联的类型
}

Iterator trait仅要求实现了一个方法: next
next:
每次返回迭代器的一项
返回结果包裹在Some里
迭代结束,返回None

可以直接在迭代器上调用next方法

#[cfg(test)]

mod tests {
    #[test]
    fn iterator_demos() {
        let v1 = vec![1, 2, 3];
        let mut v1_iter = v1.iter();

        assert_eq!(v1_iter.next(), Some(&1));
    }
}

几个迭代方法:
'iter’方法: 在不可变引用上创建迭代器
into_iter方法: 创建的迭代器会获得所有权
iter_mut方法:迭代可变的引用

2.消耗迭代器的方法

在标准库中 iterator trait有一些带默认实现的方法
其中有一些方法会调用next方法, 实现

调用next的方法叫做“消耗型迭代器”
例:sum方法:
取得迭代器的所有权
通过反复调用next,遍历所有元素

产生其他迭代器的方法
定义在Iterator trait上的另外一些方法叫做:迭代器适配器----把迭代器转换为不同种类的迭代器

可以通过链式调用使用多个迭代器适配器来执行复杂的操作,这种调用可读性较高
例 map方法
—接收一个闭包,闭包作用于每个元素
—产生一个 新的迭代器

#[cfg(test)]

mod tests {
    #[test]
    fn iterator_sum() {
        let v1 = vec![1, 2, 3];
        // _ 的意思是让编译器推断出类型
        // collect()方法的作用是: 一个消耗型适配器,把结果收集到一个集合类型中
        let v2: Vec<_> = v1.iter().map(|x| x+1).collect();
        
        assert_eq!(v2, vec![2, 3, 4]);

    }
}

3.使用闭包捕获环境

filter方法:
-接受一个闭包
-这个闭包在遍历迭代器的每个元素时,返回bool类型
-返回true:当前元素会包含在filter产生的迭代器中

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

fn shoe_in_my_size(shoe: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
    shoe.into_iter().filter(|x| x.size == shoe_size).collect()
}

#[test]

fn filter_by_size() {
    let shoes = vec![
        Shoe {
            size: 10,
            style: String::from("sneaker"),
        },
        Shoe {
            size: 11,
            style: String::from("sneaker"),
        },
        Shoe {
            size: 10,
            style: String::from("sneaker"),
        }
    ];
    let in_my_size = shoe_in_my_size(shoes, 10);
    
    assert_eq!(
        in_my_size,
        vec![
            Shoe {
                size: 10,
                style: String::from("sneaker"),
            },
            Shoe {
                size: 10,
                style: String::from("sneaker"),
            },
        ]
    )
} 

4. 使用Iterator trait 来创建自定义迭代器

实现next方法

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 calling_next_directly() {
    let mut counter = Counter::new();

    assert_eq!(counter.next(), Some(1));
}

#[test]
fn using_other_iterator() {
//zip  拉链  将俩个迭代器合在一起, 形成一个元组
    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);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值