Rust 迭代器的常用方法(1)

270 阅读2分钟

0x00 开篇

应很多粉丝要求介绍下迭代器的一些常用方法,本篇文章将介绍下 filter_mapflat_mapscan 迭代器的使用方法。

0x01 filter_map

带有 map 的适配器,一般适合一个进项对应一个出项的场景。filter_mapfiltermap 的组合。下面是 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 可以看做是 mapfilter_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 迭代器的扩展吧,大家在使用迭代器时应根据需求来选取不同的迭代器。