Rust入门笔记 6. 迭代器方法

yyi
yyi
2024-02-22 / 0 评论 / 37 阅读 / 正在检测是否收录...

在这段时间练习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

评论 (0)

取消