0x00 开篇
应很多粉丝要求介绍下迭代器的一些常用方法,本篇文章将介绍下 filter_map
、flat_map
、scan
迭代器的使用方法。
0x01 filter_map
带有 map 的适配器,一般适合一个进项对应一个出项的场景。filter_map
是 filter
和 map
的组合。下面是 filter_map
的源码:
// iterator.rs
fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
where
Self: Sized,
F: FnMut(Self::Item) -> Option<B>,
{
FilterMap::new(self, f)
}
它接收一个闭包类型,返回一个 Option
类型。如果闭包返回 None
,就表示从迭代器中取出该项,反之返回 Some(x)
,表示保留 x
值 。下面是取出迭代器中是奇数值的源码:
#[test]
fn filter_map_test() {
let vec = vec![1, 2, 3, 4, 5, 6];
let x: Vec<_> = vec.iter().filter_map(|item|
if item % 2 == 0 {
None
} else {
Some(item)
}
).collect();
dbg!(x);
}
// 运行结果
// x = [
// 1,
// 3,
//]
0x02 flat_map
flat_map
可以看做是 map
和 filter_map
的组合。下面看下源码的定义:
// iterator.rs
fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where
Self: Sized,
U: IntoIterator,
F: FnMut(Self::Item) -> U,
{
FlatMap::new(self, f)
}
flat_map
的闭包返回的是一个迭代器,也就是说是返回的一个序列。下面是将多个集合合在一起的示例代码:
#[test]
fn flat_map_test() {
let mut map = HashMap::new();
map.insert("animals", vec!["dog", "cat", "pig"]);
map.insert("fruits", vec!["apple", "orange", "banana"]);
map.insert("cities", vec!["Beijing", "Shanghai", "Guangzhou"]);
let vec = vec!["animals", "fruits", "cities"];
let result: Vec<&Vec<&str>> = vec.into_iter().flat_map(|item| {
map.get(item)
}).collect();
dbg!(result);
}
// 运行结果
// result = [
// [
// "dog",
// "cat",
// "pig",
// ],
// [
// "apple",
// "orange",
// "banana",
// ],
// [
// "Beijing",
// "Shanghai",
// "Guangzhou",
// ],
// ]
0x03 scan
scan
适配器也类似于 map
。它可以传给闭包一个可以修改的值,并且你可以随时选择终止迭代器的迭代。下面是 scan
的源码定义:
// iterator.rs
fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where
Self: Sized,
F: FnMut(&mut St, Self::Item) -> Option<B>,
{
Scan::new(self, initial_state, f)
}
scan
接收一个初始值状态和一个闭包,闭包又接收一个对初始值状态的可修改引用和迭代项。闭包的返回值是 Option
。如果闭包返回 None
则将终止迭代。
下面代码展示了,求前迭代器的平方和如果大于 50 则终止迭代,并将终止迭代前的结果返回。
#[test]
fn scan_test() {
let vec = vec![1, 2, 3, 4, 5];
let result: Vec<i32> = vec.iter().scan(0, |st, item| {
let x = item * item;
*st += x;
if *st > 50 {
None
} else {
Some(x)
}
}).collect();
dbg!(result);
}
// 运行结果
// result = [
// 1,
// 4,
// 9,
// 16,
// ]
0x04 小结
本文介绍的三种迭代器都与 map
迭代器类似,但是它们又存在一些差异,算是基于 map
迭代器的扩展吧,大家在使用迭代器时应根据需求来选取不同的迭代器。