在这段时间练习Rust过程中,大量的简单易用的迭代器方法让我觉得非常优美(尽管这么写起来可能一点都不好读),决定单开一篇整理一下迭代器的方法
迭代器是什么
所有实现了特征Iterator的类型都算迭代器,即使还没用过迭代器,但是只要用过for循环,就隐式的使用过迭代器了。
Rust的for循环是一个语法糖,in后面的对象需要是一个迭代器,for循环反复调用迭代器的next,直到Next返回一个None。
为什么要用迭代器而不直接用下标?其实Cpp的STL也大量的使用迭代器,在Rust里,我觉得主要原因是迭代器保证了不会出现越界问题。Rust保证安全性问题是有开销的,Vec每次下标访问都会检查边界,如果超出边界则panic
我们平时常用的 a..b, a..=b 也都是迭代器,或者说,它们都是Range,同时实现了Iterator迭代器
迭代器方法
进入正题,以下列举迭代器方法
首先是取元素以及遍历方法
next
next方法是迭代器最基本的功能,让迭代器指向第一个对象,返回一个 Option,它不需要参数,只需要一个Self
let a = vec![1, 2];
let mut iter = a.iter();
assert_eq!(Some(&1), iter.next());
assert_eq!(Some(&2), iter.next());
assert_eq!(None, iter.next());
enumerate
常用python的话对这个也不会陌生,enumerate同时返回迭代次数和对象,返回值为Option<(usize, Self::Item)>(的一个迭代器),相当于同时帮我们记录了迭代次数。比较常用的情况就是同时需要数组值和下标。
fn main() {
let vec = vec![222, 546, 231, 666, 10];
for (i, val) in vec.iter().enumerate() {
println!("{} : {}", i, val);
}
}
0 : 222
1 : 546
2 : 231
3 : 666
4 : 10
for_each()
for_each实际上调用的是fold,它接受一个闭包作为参数,并把每次迭代出的值传递给闭包,作为闭包的参数,它没有返回值(或者说返回值是单元Unit())
fn main() {
(1..=3).for_each(|x| {
print!("{}, ", x);
});
}
// 1, 2, 3,
take()
take接受一个参数k,表示取迭代器的前k个元素,返回的是一个被截断过的迭代器
这个方法会获取所有权
fn main() {
(1..=10).take(4).for_each(|x| {
print!("{}, ", x);
});
}
// 1, 2, 3, 4,
nth()、nth_back()
返回的是剩余元素中第k个(倒数第k)位置的元素,并跳到(k的)下一个位置,中间所有的元素都会被消费
fn main() {
let vec = vec![222, 546, 231, 666, 10];
let mut iter = vec.iter();
println!("{:?}", iter.nth(2));
println!("{:?}", iter.nth(0));
assert_eq!(iter.nth(0), Some(&10));
assert_eq!(iter.nth(0), None);
}
// Some(231)
// Some(666)
fn main() {
let vec = vec![222, 546, 231, 666, 10];
let mut iter = vec.iter();
println!("{:?}", iter.nth_back(2));
println!("{:?}", iter.nth(1));
assert_eq!(iter.nth(0), None);
}
// Some(231) , 此时231, 666, 10都被消费了,iter还在开头
// Some(546)
last()
只取迭代器的最后一个元素,会消费整个迭代器。当然,返回的也是Option\<Self::Item\>
以下是变换方法
rev()
翻转整个迭代器
vec![0, 1, 2, 3, 4].iter().rev().for_each(|x|print!(", "));
// 4, 3, 2, 1, 0,
skip(k)
跳过k个元素,相当于take的补
assert_eq!(vec![2,3], (0..4).skip(2).collect::<Vec<_>>());
step_by(k)
调整迭代器步长
//0, 2, 4, 6, 8, 10,
(0..=10).step_by(2).for_each(|x| print!(", "));
zip()
把两个迭代器合并为一个元组的迭代器
let it = [1,3,5].iter().zip([2,4,6].iter());
it : vec![(&1,&2),(&3,&4),(&5,&6)]
filter()
接受一个闭包,把元素一个个传给闭包,该闭包返回一个个bool,留下所有闭包返回true的元素,组成一个迭代器
(0..=10).filter(|x| x % 3 == 0)
// 还是个迭代器,[0, 3, 9]
collect()
我意识到我要先说collect,不然只能用for说明结果还是迭代器
collect把一个迭代器的元素组成一个新的集合,具体组合成啥由程序员指定或者由编译器推导(Rust的推导能力我觉得还是很强大的)
let vec = (0..=100).collect::<Vec<_>>();
// 默认i32,0-100的一个vec
max()、sum()、min()、count()
找到迭代元素里的最大值和求和(最小值、元素个数),sum需要指定结果类型
let vec = vec![1, 5, 3, 4, 2];
let max = vec.iter().max().unwrap(); // 5
let sum = vec.iter().sum()::<i32>(); // 15
all()、any()
接受一个闭包,和filter一样的,区别是这两个是返回一个bool而不是一个迭代器,分别代表是否所有元素都满足闭包、是否由元素满足闭包
find()
接受一个和filter一样的闭包,区别是filter返回全部的迭代器,find返回第一个满足条件的元素
map()、filter_map()
map()接受一个闭包,把元素逐个传进闭包,把闭包的返回值组成一个迭代器。
对于filter_map()来说,不会把None的东西压进迭代器,实现filter的能力
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// 使用 filter_map 方法将所有偶数映射为它们的平方根
let results: Vec<_> = numbers.iter().filter_map(|&x| {
if x % 2 == 0 {
Some((x, (x as f64).sqrt()))
} else {
None
}
}).collect();
println!("Filtered and mapped: {:?}", results);
}
flat_map()
flat_map也是接受一个闭包,不过返回的是一系列迭代器,并把他们拍扁成一个迭代器
fn main() {
let words = vec!["hello", "world", "rust"];
let chars: Vec<_> = words.iter().flat_map(|&word| word.chars()).collect();
println!("{:?}", chars); // ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'r', 'u', 's', 't']
}
partition()
根据闭包的布尔条件分拆成两个集合
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// 使用 partition 方法将奇数和偶数分开
let (even, odd): (Vec<_>, Vec<_>) = numbers
.into_iter()
.partition(|&x| x % 2 == 0);
println!("Even numbers: {:?}", even); // [2, 4]
println!("Odd numbers: {:?}", odd); // [1, 3, 5]
}
scan()、fold()
scan和fold都接受两个参数,分别是初始值和一个闭包,传入闭包两个参数:累加值和当前元素
区别是scan会把每次闭包返回的结果放一起,最后生成一个迭代器,而fold返回最终的结果
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// 使用 scan 方法计算累加和
let cumulative_sum: Vec<_> = numbers.iter().scan(0, |acc, &x| {
*acc += x;
Some(*acc)
}).collect();
println!("{:?}", cumulative_sum); // [1, 3, 6, 10, 15]
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
let sum = numbers.iter().fold(0, |acc, &x| acc + x);
println!("Sum: {}", sum); // 输出 15
}
评论 (0)