• Rust迭代器功能的10个使用示例
  • 发布于 2个月前
  • 583 热度
    0 评论
Rust迭代器功能强大且灵活。一旦掌握了它们,你就可以写出简洁灵活的代码。下面是展示Rust迭代器功能的10个示例。

1,让我们从一个简单的示例开始,把从1到10的数的平方和相加
范围(1..=10)本身被用作这里的初始迭代器,产生数字1到10。然后我们使用map方法和闭包将参数与自身相乘,生成参数的平方。Map返回一个包含平方和列表的新迭代器,我们用求和法把所有的平方和加起来,Sum返回一个值。
fn main() {
    let sum_of_squares: i32 = (1..=10).map(|x| x * x).sum();
    println!("Sum of squares: {}", sum_of_squares);
}
2,使用filter方法在特定范围内过滤出偶数
Filter是一个方法,它接受一个条件闭包,并且只返回闭包返回true的元素。filter返回另一个迭代器,这就是为什么我们在最后调用collect来将所有元素收集到Vec中。
fn main() {
    let even_numbers: Vec<_> = (1..=10).filter(|x| x % 2 == 0).collect();
    println!("Even numbers: {:?}", even_numbers);
}
3,在这个例子中,我们使用std::iter模块中的辅助函数successors来计算著名的斐波那契数列
successors创建了一个带有两个参数的迭代器,第一个参数是开始值,另一个参数是每次从迭代器中获取另一个元素时调用的闭包。闭包将前一个元素作为唯一参数,该值被包装在一个Option中,因此当迭代器中没有后继对象时,它可以发出信号。闭包使用元组(a,b)来跟踪前一个值以计算下一个值。这就是map添加到迭代器链的原因,Map接受元组,并且只返回每个元组的第一个元素。我们使用take将迭代次数限制为10次。
fn main() {
    let fib = std::iter::successors(Some((0, 1)), |(a, b)| Some((*b, *a + *b)))
        .map(|(a, _)| a)
        .take(10);
    for num in fib {
        println!("{}", num);
    }
}
4,如何计算质数
这里我们创建了一个helper函数来检查它的参数是否为质数,is_primeis使用迭代器方法all,它使用迭代器并接受一个闭包,该闭包检查所有元素。顾名思义,只有闭包对所有元素都返回true时,all方法才返回true。这里all用于检查输入数n是否不能被2到根号(n)之间的任何数整除,在这种情况下,这个数是素数。在main函数中,我们使用带有is_prime函数作为条件的filter来过滤素数。最后使用collect创建一个Vec。
fn is_prime(n: usize) -> bool {
    (2..=(n as f64).sqrt() as usize).all(|i| n % i != 0)
}
// 堆代码 duidaima.com
fn main() {
    let primes: Vec<_> = (2..=30).filter(|x| is_prime(*x)).collect();
    println!("Primes: {:?}", primes);
}

5,扁平化一个嵌套向量
使用into_iter方法将包含vector的输入向量本身转换为迭代器。然后使用flatten方法遍历它,将它扁平化为单个迭代器。最后用collect方法转换成一个新的向量。
fn main() {
    let nested_vectors = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
    let flattened: Vec<_> = nested_vectors.into_iter().flatten().collect();
    println!("Flattened: {:?}", flattened);
}
6,枚举一个字符串列表
这里我们使用enumerate方法创建一个迭代器,该迭代器生成包含输入参数的索引和索引值的元组。这里使用for_each方法对迭代器的每个元素调用闭包。
fn main() {
    vec!["apple", "banana", "cherry", "date", "fig", "grape"]
        .into_iter()
        .enumerate()
        .for_each(|(i,w)| {println!("{}.{}",i+1,w)});
}
7,两个数学向量a和b的和
在这个例子中,我们使用zip方法将两个迭代器相互交织,创建一个新的迭代器,使用map生成每个迭代器中元素的元组,这些元组元素被添加在一起。结果将被收集到一个包含向量a+b的新Vec中。
fn main() {
    let vec1 = vec![1, 2, 3];
    let vec2 = vec![4, 5, 6];
    let result: Vec<_> = vec1.into_iter()
      .zip(vec2.into_iter()).map(|(a, b)| a + b).collect();
    println!("Sum: {:?}", result);
}
8,将一个列表折叠成一个值
使用fold方法可以遍历迭代器中的所有元素,fold有两个参数,第一个是初始值,第二个是一个有两个参数的闭包:初始值和当前值。每次迭代的初始值都是前一次调用的返回值。顾名思义,将列表最后折叠成一个值。我们也可以使用product方法代替fold方法。
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let product: i32 = numbers.into_iter().fold(1, |acc, x| acc * x);
    // 可以使用product()代替
    // let product: i32 = numbers.into_iter().product();
    println!("Product: {}", product);
}
9,找出第一个大于3的数
find方法接受一个闭包,并返回闭包结果为true的第一个元素,闭包本身接受一个参数。一个类似的方法是position,它返回第一个闭包结果为true的位置,而不是闭包为真的值。
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let found = numbers.iter().find(|&&x| x > 3);
    // 返回位置
    // let found = numbers.iter().position(|&x| x > 3);
    println!("First number greater than 3: {:?}", found);
}
10,创建一幅扑克牌
flat_map方法用于迭代器,将映射操作与扁平化操作结合起来。在我们的例子中,我们使用它来为一副标准的52张扑克牌创建所有可能的等级和花色组合。这个例子比其他例子稍微复杂一点,所以这里有它的工作原理的详细描述。正如你在这里看到的,你也可以迭代普通数组。
fn main() {
    let suits = ["C", "D", "H", "S"];
    let ranks = [
        "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A",
    ];

    let deck: Vec<String> = suits
        .iter()
        .flat_map(|suit| {
            ranks
                .iter()
                .map(move |rank| format!("{}{}", rank, suit))
        })
        .collect();

    println!("Deck of cards: {:?}", deck);
}
对于suits数组中的每一种花色,flat_map接受一个闭包,该闭包接收花色作为参数(在本例中,它是|suit|)。在闭包内部,我们使用ranks.iter()创建一个新的迭代器。这个迭代器遍历rank数组中的所有rank。

然后在ranks迭代器上应用map方法。闭包'move |rank|format!("{}{}", rank, suit)'为每个rank执行。move关键字用于按值捕获suit变量,允许它在闭包中使用。format!宏用于通过连接rank和花色来为每张牌创建字符串表示。最后,flat_map将所有这些单独的suit迭代器组合成一个迭代器,有效地将结构变得平坦。结果迭代器现在产生所有52张扑克牌。


总结
Rust迭代器是一个功能强大的构造,它允许开发人员以一种高度表达、高效和可组合的方式处理数据序列。
以下是Rust迭代器最重要的优点和特性:
1,可组合性:迭代器可以使用map、filter、flat_map、chain、zip等方法轻松组合。这使得开发人员可以用简单易读的代码创建复杂的数据转换。
2,惰性求值:迭代器在使用值时实时计算值,确保只处理所需的元素。这会带来显著的性能提升,特别是在处理大型或无限数据集时。
3,可读性:通过使用迭代器方法,开发人员可以编写与他们要解决的问题非常相似的代码。这使得代码更易于阅读和理解,从而减少错误和提高代码的可维护性。
4,灵活性:Rust迭代器可用于广泛的数据结构,如数组、向量、链表等。它们还支持自定义数据类型,允许开发人员为特定的用例创建自己的迭代器实现。
5,抽象:迭代器抽象了数据遍历的细节,使你能够专注于对数据执行的操作,而不是遍历本身的机制。这样可以得到更清晰、更易维护的代码。另一方面,循环通常需要手动索引或迭代,这可能会导致错误或其他问题。
6,统一接口:迭代器为处理不同类型的数据结构提供了一致的接口。这使得编写可以处理各种数据类型(如数组、向量、字符串和链表)的泛型代码变得更容易。传统的循环对于不同的数据结构通常需要不同的方法,这可能会使代码更加复杂和难以维护。
7,减少错误:通过抽象出数据遍历的细节,使用迭代器可以帮助减少常见错误的可能性,例如位置差一错误或索引越界问题。这使得开发人员可以专注于数据处理逻辑,而不是管理循环变量和索引。
8,性能:Rust迭代器在设计时就考虑到了性能。Rust编译器内联了许多迭代器方法,与传统循环相比,这可以显著提高性能。
用户评论