Rust学习笔记

迭代器

迭代器是一个值,它可以生成一系列值

Iterrator 类型和 IntoIterator类型

  • 迭代器是实现了Iterator类型的任意值
  • IntoIterator是迭代器本身类型,Item是它生成的值的类型。
  • 任意实现了IntoIterator的类型都可以成为可迭代者,可以通过into_iter获得一个迭代器
  • 迭代器能生成值
  • 迭代器生成的值是Item(条目)
  • 接收迭代器条目的代码是消费者
  • 大多数集合提供了iter和iter_mut方法,会返回该类型的迭代器,为每个条目生成共享引用或可变引用
  • 给一个集合的共享引用,into_iter会返回一个条目的共享引用

  • 给一个集合的可变引用,into_iter会返回一个条目的可变引用

  • 当按值传递集合,into_iter会返回一个迭代器,获得集合的所有权,按值返回。这些值的所有权会传递给消费者,在迭代过程中消耗掉。

  • 并非所有类型都提供了这3种实现,HashSet,BTreaSet,BinaryHeap不会在可变引用上实现IntoIterator,修改元素会破坏自身不变性规则-修改后的值可能有不同的hash。

from_fn 和 successors

from_fn:给定返回Option的函数,std::iter::from_fn会返回一个迭代器,它会调用fn来生成条目

successors:如果每个条目依赖前一个条目,std::iter::successors比较适合。提供一个初始条件和一个函数,且该函数能接受一个条目并返回下一个条目的Option。如果返回None则结束。

from_fn与successors都接受FnMut闭包

fn fibonacci() - > impl Iterator <Item=usize>{
  let mut sate = (0,1);
  std::iter::from_fn(move ||{
    state = (state.1, state.0 + state.1);
    Some(state.0)  
})
}

assert_eq!(fibonacci().take(8).collect::<Vec<_>>(),
                      vec![1,1,2,3,5,8,13,21]);

from_fn successors 方法非常灵活,你可以将任何对迭代器的使用用它们来改写。
在使用这两个方法前,请确保你了解其他的迭代器不是最优的选择。

drain

许多集合提供了drain方法(抽取方法)。drain会接受一个对集合的可变引用,返回一个迭代器,将每个元素的所有权传给消费者。
与into_iter不同,drain只会借入对集合的可变引用,当迭代器丢弃时,drain会从集合中移除抽取范围的所有元素,无论drain是否会被消费者消耗。

map与filter

map是映射适配器,能针对迭代器的各个条目来调用闭包帮你转换迭代器。filter是过滤器,能使用闭包帮你从迭代器中过滤某些条目,由闭包决定保留和丢弃哪些条目。

map会按值传递每个条目给闭包,将闭包的结果的所有权转移给map的消费者。filter会共享引用每个条目给闭包,保留所有权,过滤后将所有权传递给filter的消费者。

要点1:单纯在迭代器上调用适配器不会消耗任何条目,只会返回一个新的迭代器。在适配器链中,实际完成任何工作的唯一方法是调用next。
要点2:迭代器的适配器是一种零成本抽象,由于map,filter和其他类似的适配器都是泛型,因此它们应用与迭代器就会专门针对涉及的特定类型生成特定代码。

filter_map和flat_map

filter_map与map类似,但它允许其闭包将条目转换为新条目(与map一样)或从迭代中丢弃该条目。闭包会返回一个option,当返回None时,该条目将从迭代中丢弃。
filter_map的作用在于,需要在闭包中尝试处理一下条目,看看是否ok,如果是None就丢弃,是ok的就保留。
等同于map(|| do_some()).filter(|| is_ok()).map(|| unwrap())

flat_map的功能是不仅仅像map一样返回一个条目,还可以返回任意数量的条目序列。flat_map的闭包必须返回一个可迭代者,比如一个Vec。

flatten

flatten适配器会串联迭代器的各个条目,这些条目应该都是可迭代者。例如在一个hashmap里保存vec序列作为值。调用flatten将会平铺整个结构。
flatten会返回一个迭代器,这个迭代器会生成一个一级条目的串联序列。

一个用法:如果只想从一个Vec<Option<...>>中迭代出Some值,可以这样用

vec![None,Some("day"),None,Some("one")].into_iter().flatten().collect:<Vec<_>>();
会返回
vec!["day","one"]

因为Option也实现了IntoIterator,表示有0个或1个元素组成的序列,None对迭代没有贡献,Some会贡献一个值。同样的,也可以用flatten迭代
Option<vec<...>>,None表示一个空向量。

有一些时候使用了map().flatten(),可以用flat_map来替代。

take与take_while

take与take_while是取出一定数目的条目,take_while可以决定终止的条件。

skip和skip_while

skip 和skip_while是跳过一定数目的条目,skip_while是可以决定停止跳过的条件。

peakable

peakable可以让我们先看看即将生成的下一个条目是啥,不需要消耗它。

peakable有一个额外的peek方法,会返回一个Option,如果迭代器耗尽则返回None。调用peek会尝试取下一个条目,如果存在将其缓存,直到调用next时给出。

fuse

fuse(保险丝)适配器能接受任何迭代器,并生成一个确保在第一次返回None后继续返回None的迭代器。

就是当一个迭代器发生了意外,这个意外可能会随着迭代器的迭代而溜掉,fuse可以让出现第一个None后,无论后续是不是Some,所有的迭代都返回None。

rev

有的迭代器可以从序列的两端抽取条目,使用rev可以反转序列迭代顺序。

inspect

inspect适配器是一种探针,为调试适配器流水线提供了便利。inspect接受闭包,但不会影响条目,可以打印一些信息。

chain

chain适配器可以将一个迭代器追加另一个迭代器后面。从第一个迭代器抽取条目,完成后继续从第二个迭代器抽取条目。

你可以将迭代器与任何会生成相同条目类型的可迭代者链接在一起。

enumerate

枚举适配器会将索引附加到序列中,它接受某个迭代器生成的条目a,b,c...,并返回一个新的条目序列(0,a),(1,b),(2,c)...
enumerate在HashMap中,获得是(key,value)。

zip

zip适配器会将两个迭代器组合成一个迭代器,新的迭代器会生成一对值,每个迭代器提供一个值组成一对,类似拉链拉合在一起。

by_ref

迭代器的by_ref方法会借入迭代器的可变引用,便于将各种适配器应用与该引用。这些适配器用完后,你重新可以获得原始迭代器的访问权。
避免适配器消耗迭代器的条目

cloned与copied

cloned适配器会接受一个生成引用的迭代器,并返回一个会生成从这些引用的克隆值的迭代器。将引用变成值。
copied比cloned更加严格,要求引用目标必须实现了copied

cycle

cycle适配器会返回一个迭代器,将无限循环生成序列。调用cycle的迭代器必须实现了clone,便于cycle保存其初始状态并在每次循环重新开始时调用它。

迭代器的消耗

累加

count 会从迭代器中取出条目,直到返回None,报告取出条目的数量
sum 和product 两个方法会分别计算迭代器条目的和和乘积

最小值与最大值

min 与 max 返回迭代器的最小和最大值 ,条目类型必须实现std::cmp::Ord,否则无法比较大小。

如果知道如何处理NaN值,就可以利用 max_by 和min_by,可以提供自己的比较函数。

min_by_key 和 max_by_key 可以按照键值来比较选择最小和最大值,它们接收一个闭包,闭包返回任意有序类型B,这就是需要比较的键值。

对条目序列比较

如果字符串,向量和切片的各个元素是可以比较的,可以使用< == 等运算符比较它们大小,不过运算符不能用作迭代器的比较,有个专门的消耗器
eq lt le gt ge 用于从迭代器中取出条目并比较。

any 和 all

any 从迭代器中查询任意让闭包中条件为true的条目,如果有这种条目,返回true,否则返回fale
all 是从迭代器中查询所有条目,可以让闭包中的条件为true,如果所有条目都满足条件,则返回true,否则返回false

position rposition ExactSizeIterator

position 会针对迭代器每个条目调用闭包,返回调用结果为true的第一个条目的索引的Option,否则返回None

rposition 是从右边开始查询每个条目。它要求使用可逆的迭代器,这样才能从右边开始查询。同时它也要求这个迭代器是ExactSizeIterator
大小要确定,以便能查询到索引值。

fold rfold

fold是常用的消耗器,用于在迭代器上生成序列累积的结果。需要给定fold一个初始值和闭包,fold会以当前累加值和下一个值为参数反复调用闭包。
最终累加值会返回,如果序列为空,只能返回初始值。

fold的闭包是两个参数,例如 fold(0,|n,i| n + i ) ,0是初始值,n是累加变量,i是下一个条目值。

rfold是从右边开始累积。

try_fold try_rfold

尝试折叠序列,有fold的不同是可以提前退出,不用消耗掉所有迭代器的值。try_fold的闭包的返回值会指出它应该是立即返回还是继续折叠。

闭包的返回类型有多种。

  • 如果返回是Result<T,E>,返回Ok(v)则继续折叠,返回Err(e)则退出
  • 如果返回是Option,则返回Some(v)继续折叠,返回None则退出
  • 闭包还可以返回一个std::ops::ControlFlow值,有两个变体,Continue(c)和Break(b),如果消耗了所有的迭代器的条目,则返回Continue(v)。如果中途返回,则返回Break(b)
    使用ControlFlow类型的优点是,有时候中途退出不是一种错误。

fold总会消耗掉所有的迭代器的条目,很多场景都可以用try_fold来处理特殊情况。

nth与nth_back

nth会接受一个n作为参数,从迭代器中跳过n个条目,返回下一个。如果提前结束返回None
nth不会获得条目所有权

nth_back是获得倒数第n个条目

last

获得最后一个条目

find rfind 和 find_map

find会从迭代器中取出条目,返回让闭包中条件为true的条目。

rfind会从右边开始寻找。

find_map的闭包不会返回bool类型,返回某个值的Option

collect

collect用于构建迭代器条目的集合。collect本身不知道如何构建这些类型的集合,某些集合类型知道如何从迭代器中构建自己,会实现std::iter::FromIterator
collect只是便捷的封装。

extend

extend可以将一些可迭代者添加到集合中,扩展这个集合。

partition

将迭代器的条目划分到两个集合中,闭包来决定每个条目的位置,如果闭包判断条件为true,加入第一个集合,false则加入第二个集合

for_each try_for_each

for_each 会对每一个条目调用某个闭包,与for循环有点类似。

如果闭包需要容错或提前退出,可以使用try_for_each。

集合

Rust的8个标准集合

Vec 可增长数组
VecDeque 双端队列(可增长的环形缓冲区)
LinkList 双向链表
BinaryHeap T:Ord 二叉堆(始终可以高效查找最大值)
HashMap<K,T> K:Eq+Hash 哈希表
BtreeMap<K,T> K:Ord 有序键值表
HashSet T:Eq+Hash 无序,哈希集合
BTreeSet T:Eq 有序集合

posted on 2024-01-28 23:06  Tmacy  阅读(15)  评论(0编辑  收藏  举报

导航