您的位置:首页 > 教育 > 锐评 > 19.闭包与迭代器(下)

19.闭包与迭代器(下)

2024/10/5 14:44:52 来源:https://blog.csdn.net/wzj_sxpi/article/details/139815420  浏览:    关键词:19.闭包与迭代器(下)

标题

  • 二、迭代器
    • 2.1 概念
    • 2.2 Iterator trait和next方法
      • 1) Iterator trait方法
      • 2) next方法
    • 2.3 消耗迭代器的方法
    • 2.4 产生其他迭代器的方法
    • 2.5 使用闭包捕获环境
    • 2.6 创建自定义迭代器
  • 三、改进/IO项目
    • 3.1 Config结构体使用迭代器
    • 3.2 main函数
    • 3.3 search函数的修改

二、迭代器

2.1 概念

  • 迭代器(iterator) 是负责遍历序列中的每一项和决定序列何时结束的逻辑;
  • Rust中的迭代器是惰性的(lazy),除非调用方法,否则它不会有任何效果;
fn main() {let v1 = vec![1, 2, 3];let v1_iter = v1.iter();for val in v1_iter{println!("Got: {}", val);}
}
  • v1_iter是个迭代器,没有使用的话它不会有任何作用;
  • 接下来采用for循环打印迭代器里的值;

2.2 Iterator trait和next方法

1) Iterator trait方法

  • 所有迭代器都实现了Iterator trait,其定义如下
pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;// 此处省略了方法的默认实现
}
  • type ItemSelf::Item新语法定义的trait的关联类型(associated type)
  • 实现 Iterator trait需要同时定义一个Item类型,它是迭代器返回元素的类型;

2) next方法

  • next是Iterator实现者被要求定义的唯一方法
  • next方法调用时一次返回迭代器中的一个项,封装在 Some 中,当迭代器结束时,它返回 None;
#[cfg(test)]
mod tests{#[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);}
}
  • 上面的代码测试通过
  • 迭代器v1_iter必须是可变的:迭代器上调用next方法改变了迭代器中用来记录序列位置的状态;
  • 从next调用中得到的值是vector的不可变引用
  • 获取v1所有权并返回拥有所有权的迭代器,使用into_iter,迭代可变引用需调用iter_mut

2.3 消耗迭代器的方法

  • Iterator trait有一些默认的调用了next方法,统称为消耗型适配器(consuming adaptors)
fn main() {let v1 = vec![1, 2, 3];let v1_iter = v1.iter();let total: i32 = v1_iter.sum();println!("total = {}", total);  //total = 6
}
  • sum方法就是一种消耗型适配器的方法
  • sum会获取迭代器的所有权,因此调用之后不能再使用v1_iter;

2.4 产生其他迭代器的方法

  • 迭代器适配器允许将迭代器转换为不同类型的迭代器;
  • 迭代器适配器支持链式调用;
#[cfg(test)]
mod tests{#[test]fn iterator_demonstration() {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]);}
}
  • iter()方法产生迭代器;
  • 再使用map方法使用闭包调用每个元素生成新的迭代器;
  • 迭代器对vector中的每个元素都加1;

2.5 使用闭包捕获环境

  • filter接收一个闭包;
  • 这个闭包在遍历迭代器的每个元素时,返回bool类型;
  • 如果闭包返回true,则当前会包含在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()
}#[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") },]);
}

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

  • 先调用into_iter()方法生成一个迭代器;
  • 再调用filter方法传入闭包,看鞋号和传入的鞋号是否相等,如果相等则放到新迭代器中;
  • 最后调用collect()形成集合;
  • 最后过滤出10号鞋子,测试通过;

2.6 创建自定义迭代器

  • 可以在vector上调用iter、into_iter或iter_mut创建一个迭代器;
  • 可以用诸如map的集合类型创建迭代器;
  • 可以实现Iterator trait自定义迭代器;
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> {self.count += 1;if self.count < 6 {Some(self.count)} else {None}}
}#[test]
fn calling_next_directly() {let mut counter = Counter::new();assert_eq!(counter.next(), Some(1));assert_eq!(counter.next(), Some(2));assert_eq!(counter.next(), Some(3));assert_eq!(counter.next(), Some(4));assert_eq!(counter.next(), Some(5));assert_eq!(counter.next(), None);
}

创建一个能从1遍历到5的迭代器

  • 创建一个Counter结构体,内含迭代过程中的计数变量;
  • 为Counter实现next方法,Ttem = u32说明返回值是u32类型;
  • calling_next_directly的测试从1到5,最后返回None;

再创建一个迭代器从2开始,即为2,3,4,5,要求产生新的迭代器中的值能被3整除,将满足要求的元素求和

#[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);
}
  • 先new一个对象;
  • 然后调用zip方法将前后两个迭代器组合到一起,内容形成一个元组,元组的内容分别来源于两个迭代器;
  • 使用map将两个元组内的值相乘并产生一个新的迭代器;
  • 然后使用filter过滤相乘后能被3整除的元素;
  • 最后求和;

计算过程为

  • 第一个迭代器值为(1,2,3,4,5)
  • 第二个迭代器值为(2,3,4,5)
  • zip之后的值为(1,2) (2,3) (3,4)(4,5)
  • zip在任一输入迭代器返回None时也返回None,因此没有(5,None)
  • 对应相乘为 2,6,12,20,过滤后为6,12;
  • 再求和即为18;

三、改进/IO项目

3.1 Config结构体使用迭代器

原始代码

impl Config{pub fn new(args: &[String]) -> Result<Config, &'static str>{if args.len() < 3{return Err("Not enough arguments!");}let query = args[1].clone();let filename = args[2].clone();let case_sensitive = env::var("CASE_INSENSITIVE").is_err();Ok(Config {query, filename, case_sensitive})}
}
  • 参数args是引用类型,而Config中的query和filename必须有所有权,因此需要克隆出来;
  • 现在可以将new函数改为获取一个所有权的迭代器作为参数而不是使用切片;
  • 还可以使用迭代器附带的功能进行长度检查和索引;
  • 这样可使new的责任范围更加明确(通过迭代器将读取具体值的工作分离出去);
  • Config::new一旦获取了迭代器的所有权,就可以将迭代器中的字符串移到Config中,而不用调用clone分配新空间;

修改后代码

impl Config{pub fn new(mut args: std::env::Args) -> Result<Config, &'static str>{if args.len() < 3{return Err("Not enough arguments!");}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})}
}
  • 直接使用环境变量的迭代器作为参数,调用next得到下一个值;
  • next之后如果是None则返回错误,否则直接返回字符串;

3.2 main函数

原始代码

fn main() {let args: Vec<String> = env::args().collect();let config = Config::new(&args).unwrap_or_else(|err| {eprintln!("Problem parsing arguments: {}", err);process::exit(1);});//其他代码
}
  • Config::new需要传入std::env::Args类型;
  • 因此不需要执行collect收集命令行参数后行成集合,直接传入就行了;

修改

fn main() {let config = Config::new(env::args()).unwrap_or_else(|err| {eprintln!("Problem parsing arguments: {}", err);process::exit(1);});//其他代码
}

3.3 search函数的修改

原始代码

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
}
  • 可以使用迭代器适配器编写更简明的代码;
  • 也避免了可变中间变量results的使用;

修改

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com