《Rust编程入门》8.2 迭代器与迭代器适配器
8.2 迭代器与迭代器适配器
在 Rust 中,迭代器是一个非常强大的特性,它允许你以一致的方式访问集合中的元素。Rust 的迭代器是懒惰的,这意味着它们不会立即执行,而是会在需要时按需计算。这使得迭代器非常高效,特别是在处理大型集合时。
8.2.1 迭代器基础
迭代器是实现了 Iterator 特征的类型。Iterator 特征提供了一个核心方法 next(),它返回一个 Option<T>,每次调用都会返回集合中的下一个元素,直到没有元素可供迭代。
创建一个简单的迭代器
fn main() {
let v = vec![1, 2, 3, 4, 5];
let mut iter = v.iter(); // 创建一个迭代器
// 使用 next() 获取元素
while let Some(&val) = iter.next() {
println!("{}", val); // 输出: 1 2 3 4 5
}
}
v.iter()返回一个迭代器,它可以遍历Vec中的元素。next()每次调用返回一个Option类型,如果迭代器中还有元素,next()会返回Some(value);如果没有元素,则返回None。
使用 for 循环简化迭代
fn main() {
let v = vec![1, 2, 3, 4, 5];
// 使用 for 循环自动迭代
for val in v.iter() {
println!("{}", val); // 输出: 1 2 3 4 5
}
}
- 在 Rust 中,
for循环可以自动处理迭代器,省去了手动调用next()的麻烦。
消费者方法:sum()
Rust 提供了一些常用的迭代器方法,可以对集合进行操作并返回最终结果。sum() 是其中之一,它可以对迭代器中的所有元素进行求和。
fn main() {
let v = vec![1, 2, 3, 4, 5];
let sum: i32 = v.iter().sum();
println!("Sum: {}", sum); // 输出: Sum: 15
}
sum()通过迭代器计算集合中元素的总和。
8.2.2 迭代器适配器
迭代器适配器是链式方法,可以对迭代器执行转换、过滤和其他操作。迭代器适配器本身并不立即执行,而是返回一个新的迭代器,只有在实际遍历时,所有操作才会生效。
map 适配器
map 适配器可以将一个闭包应用到每个元素上,生成新的元素。例如,可以对每个数字进行平方操作:
fn main() {
let v = vec![1, 2, 3, 4, 5];
let squares: Vec<i32> = v.iter().map(|&x| x * x).collect();
println!("{:?}", squares); // 输出: [1, 4, 9, 16, 25]
}
map返回一个新的迭代器,其中每个元素都经过闭包的处理。collect()方法用于将迭代器的结果收集到一个集合中(此例中是Vec<i32>)。
filter 适配器
filter 适配器根据给定的条件过滤元素。例如,只保留偶数:
fn main() {
let v = vec![1, 2, 3, 4, 5];
let even_numbers: Vec<i32> = v.iter().filter(|&&x| x % 2 == 0).collect();
println!("{:?}", even_numbers); // 输出: [2, 4]
}
filter接受一个闭包,只有返回true的元素会被保留。- 这里的闭包
|&&x| x % 2 == 0用于筛选偶数。
take 和 skip 适配器
take适配器会从迭代器中返回前n个元素。skip适配器会跳过迭代器中的前n个元素,返回剩余部分。
fn main() {
let v = vec![1, 2, 3, 4, 5];
let first_three: Vec<i32> = v.iter().take(3).collect();
let after_first_two: Vec<i32> = v.iter().skip(2).collect();
println!("{:?}", first_three); // 输出: [1, 2, 3]
println!("{:?}", after_first_two); // 输出: [3, 4, 5]
}
take(3)选择前 3 个元素。skip(2)跳过前 2 个元素,返回剩余部分。
fold 适配器
fold 适配器是一个非常强大的方法,它允许你对迭代器中的元素进行累计操作,返回一个最终结果。与 reduce 类似,它从初始值开始,逐步应用闭包中的操作。
fn main() {
let v = vec![1, 2, 3, 4, 5];
let sum: i32 = v.iter().fold(0, |acc, &x| acc + x);
println!("Sum using fold: {}", sum); // 输出: Sum using fold: 15
}
fold(0, |acc, &x| acc + x)从0开始,累加每个元素的值。
zip 适配器
zip 适配器用于将两个迭代器“压缩”成一个新的迭代器,其中每个元素是一个元组,包含两个原始迭代器的元素。
fn main() {
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let zipped: Vec<(i32, i32)> = v1.iter().zip(v2.iter()).collect();
println!("{:?}", zipped); // 输出: [(1, 4), (2, 5), (3, 6)]
}
zip将两个迭代器中的元素配对成元组。
8.2.3 迭代器的惰性计算
Rust 中的迭代器是惰性(lazy)的,意味着它们不会立即执行。只有当我们真正遍历它们时,才会进行计算。通过这种方式,Rust 能够避免不必要的计算,提高性能。
例如,下面的代码展示了 map 和 filter 适配器的惰性特性:
fn main() {
let v = vec![1, 2, 3, 4, 5];
// 创建一个链式迭代器
let result: Vec<_> = v.iter().map(|&x| x * 2).filter(|&x| x > 5).collect();
println!("{:?}", result); // 输出: [6, 8, 10]
}
- 在执行时,
map和filter会依次处理元素,但没有立即产生结果,直到调用collect()才会进行所有的计算。
8.2.4 小结
- 迭代器是 Rust 中的核心特性,它允许以一致的方式访问集合中的元素,并提供了多种常用操作。
- Rust 的迭代器是惰性求值的,只有在实际遍历时才会执行计算,这有助于提高性能。
- 迭代器适配器(如
map、filter、fold等)可以对迭代器进行转换,返回一个新的迭代器,使得处理数据的方式更加灵活。
在下一节中,我们将深入探讨闭包与高阶函数,它们与迭代器和迭代器适配器紧密结合,进一步提升 Rust 的表达力和灵活性。