rust十三.2、迭代器

打印 上一主题 下一主题

主题 754|帖子 754|积分 2262

Iterator(迭代器)是一个比较常见的概念,大部分语言都有。大部分语言实现迭代器的逻辑并没有特别的。
看完了有关内容,作者的意思是:rust的迭代器和匿名函数一样,都是为了提供时下游行的函数式编程。
此二者为rust实现零成本抽象提供了不少的贡献。
本部分概念比较多,如果是编程初学者大概会有点吃力,如果是老手照旧比较轻易理解的。
一、迭代器根本概念

 问题:

  • 迭代器是什么
  • 如何创建
  • 迭代器佑什么用
1.1、迭代器是什么

迭代器:可以或许逐个获取聚集元素(以聚集元素居多,实际大概不愿定是聚集)并指向下一个元素的对象(一段代码)
迭代器是一种简单抽象,无论其代码如何实现,但本质上差不多:一个next方法(叫什么无所谓),一个指示器(也许更多,但大概就是这个意思)
1.2、如何创建
  1. pub trait Iterator {
  2.     type Item;
  3.     fn next(&mut self) -> Option<Self::Item>;
  4.     // 此处省略了方法的默认实现
  5. }
复制代码
 一个对象(结构或者什么的)只要实现这个特质就可以创建迭代器。
例如以下为特定的枚举创建一个迭代器:
  1. /**
  2. *
  3. */
  4. struct  Books{
  5.     names:String,
  6.     author:String,
  7.     content:String
  8. }
  9. impl Books {
  10.     fn iter(&mut self) -> BooksIterator {      
  11.         BooksIterator{
  12.             current_index:0,
  13.             strs:vec![self.names.clone(), self.author.clone(),self.content.clone()]
  14.         }  
  15.     }
  16. }
  17. /**
  18. * 关键之一:定义一个临时结构,以便迭代器能够访问结构中的字段,并进行查找
  19. */
  20. struct BooksIterator{
  21.     current_index:usize,
  22.     strs:Vec<String>
  23. }
  24. /**
  25. * 实现迭代器特质:Iterator
  26. */
  27. impl Iterator for BooksIterator {
  28.     type Item = String;
  29.     fn next(&mut self) -> Option<String> {
  30.         if self.current_index < 3 {
  31.             let item = self.strs[self.current_index].clone();
  32.             self.current_index += 1;
  33.             Some(item)
  34.         } else {
  35.             None
  36.         }
  37.     }
  38. }   
  39. fn main() {
  40.     let mut b = Books{
  41.         names:String::from("星星之火可以燎原"),
  42.         author:String::from("毛->泽东"),
  43.         content:String::from("新年已经到来几天了,你的信我还没有回答。
  44.         一则因为有些事情忙着,二则也因为我到底写点什么给你呢?
  45.         有什么好一点的东西可以贡献给你呢?
  46.         搜索我的枯肠,没有想出一点什么适当的东西来,因此也就拖延着")        
  47.     };
  48.     let it=b.iter();
  49.     for i in it {
  50.         println!("{}",i);
  51.     }
  52. }
复制代码
幸运的是(固然也是应该的),rust为许多应该提供迭代器的对象提供了创建迭代器的方法。
例如向量
  1. let mut height_arr: Vec<i32> = Vec::new();
  2. for i in height_arr.iter() {
  3.         println!("{}", i);
  4. }
复制代码
 
rust为向量实现了多个方法用于创建迭代器:

  • iter  - 天生一个不可变引用的迭代器
  • into_iter  --天生一个获取所有权的迭代器
  • iter_mut  --天生一个可变引用的迭代器
 
 
 
例如数组
  1. fn print_use_for_loop_3_iter(arr:&[u32]){
  2.     //注意这个是针对固定数组,如果是可变数组则可以
  3.     //for element in array.into_iter()
  4.    
  5.     println!("---- 使用 for 循环和迭代iter 打印数组元素");   
  6.     for ele in arr.iter() {  
  7.        print!("{}", ele);  
  8.     }
  9.     println!("\n");
  10. }
复制代码
 
1.3、两个告急概念:迭代器适配器消耗适配器

迭代器适配器-基于天生迭代器方法产生新的迭代器的方法,也可以迭代器包装器
消耗适配器-基于迭代器,消耗迭代器的方法,目标是基于迭代器产生一些新的结果(非迭代器)
此二者都是方法,通常都属于迭代器自生的方法,迭代器适配器天生新的迭代器,消耗适配器则是使用迭代器。
原书给出了很完善和浅显易懂的例子:
  1. -- 迭代器适配器
  2. let v1: Vec<i32> = vec![1, 2, 3];
  3. v1.iter().map(|x| x + 1);
  4. -- 消费适配器
  5. fn iterator_sum() {
  6.     let v1 = vec![1, 2, 3];
  7.     let v1_iter = v1.iter();
  8.     let total: i32 = v1_iter.sum();
  9.     assert_eq!(total, 6);
  10. }
复制代码
1.4、用处


  • 焦点用处:源码看起来更加简便一些,由于可以向所谓的函数式编程靠近。
  • 次要作用:让工程师专注于业务逻辑,由于数据怎么得到,由迭代器处置惩罚。 此作用因人而异
函数式编程有优点有缺点,这是一个编程风格。如果你不喜欢,不黑白得用不可。
阻碍项目进度的往往不是这些细枝末节,而是诸如需求,设计、测试。
如果一个项目完全不用迭代器也是完全很好的。
 
不过如果rust官方已经提供了许多的适配器,那么该用照旧要用的。
例如上文的消耗适配器sum,为什么不用了?究竟可以少打几个字母。
二、性能比较(对比循环)

结论:请放心大胆的使用迭代器和闭包吧!它们使得代码看起来更高级,但并不为此引入运行时性能损失
这是由于rust的编译器对匿名函数和迭代器的实现进行了优化。按照作者的意思就是:在许多环境中,迭代相对循环所损失的性能微乎其微。
2.1、零成本抽象

zero-cost abstractions,中文含义“零成本抽象”
意思就是:虽然我把代码写得更加简便(往往大概就是抽象了),但是我通过编译器得努力,不会由于这个抽象而损失性能
这个所谓的零成本抽象应该是rust的目标之一。
把语法搞得复杂抽象应该也是rust的目标之一,联合零抽象成本,可以告诉工程师放心使用rust。
2.2、rust编译器

我已经说了许多次:rust锋利的是编译器。大体是这个意思。
具体怎么零成本的,作者没有多说,不过提到一个概念:unroll(睁开)
这是一个“笨”办法,也是提升性能的最常用的一个方法。
这个方法就是把代码编译为多段重复的代码,大概如下:
  1. /**
  2. * 循环,非展开
  3. */
  4. fn roll(){
  5.     let arr=[1,2,3];
  6.     let mut total=0;
  7.     for i in 0..arr.len(){
  8.         total=total+arr[i];
  9.     }
  10.     println!("{}",total);
  11. }
  12. /**
  13. * 展开测试1
  14. */
  15. fn unroll_one(){
  16.     let arr=[1,2,3];
  17.     let mut total=0;
  18.     total=total+arr[0]+arr[1]+arr[2];
  19.     println!("{}",total);
  20. }
  21. /**
  22. * 展开测试2
  23. */
  24. fn unroll_two(){
  25.     let a=1;
  26.     let b=2;
  27.     let c=3;
  28.     let mut total=0;
  29.     total=a+b+c;
  30.     println!("{}",total);
  31. }
  32. fn main(){
  33.     roll();
  34.     unroll_one();
  35.     unroll_two();
  36. }
复制代码
 
rust大概会把上文中的roll改写为 unroll_two之类的代码。固然这仅仅是一个示意,本人没有研究过rustc的实现。
 
写步伐要看是处于什么目标,如果寻求工程效率,这样写肯定被骂死。
如果最求性能,这反而是相称明智有效的方法之一。
2.3、个人看法


  • 如果有rust的默认迭代器,我会考虑用用。如果没有,我不会特意去定义的
  • 出于性能等缘故原由,我更愿意使用循环,而不是迭代
  • 盼望rust团队能给rust添加更多面向对象的内容。面向对象并不会对性能造成什么影响
三、小结


  • 实现特质Iterator就可以或许创建自己迭代器
  • rust提供了许多默认的迭代器适配器、迭代器方法
  • 利用消耗适配器、迭代器适配器和匿名函数,可以或许实现函数式编程
  • 据说rust可以或许让迭代器的性能和for循环差不多
  • 迭代器是rust实现其零成本抽现象的实践之一
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表