简介
在编程中,我们经常需要计数,枚举,遍历某个序列。在各种编程语言中有很多方式可以做这些工作,例如C语言相似的语法:
for (int x = 0; i < 10; ++x) {
// do something
}
虽然这种方式非常的灵活并且能够适应各种场景,但是这种方式是非常容易导致bug的,比如放错了分号导致无意义的循环等。本着安全和一致性的原则,在Rust中并没有实现类似C语言风格的遍历方式,而是通过迭代器来实现。
基本的范围循环
在Rust中循环遍历一系列数字最常用的方式是Range
,它由 ..
创建,例如
for i in 1..11 {
println!("{}", i);
}
上面的代码将会在控制台打印1-10的数字,但是不会打印11,如果想要打印11 可以使用..=
for i in 1..=11 {
println!("{}", i);
}
如果你不需要迭代中的变量,可以直接使用_
进行省略
let mut n = 0;
for _ in 1..11 {
n += 1;
}
println!("num = {}", n);
对于上面的示例,Rust中有一个更方便的方法count()
,这个方法会返回迭代器中的元素数量而无需在循环中计数。
println!("num = {}", (0..11).count());
深入理解迭代器
filter()
filter()
接收一个闭包,这个闭包返回true或者false,返回的序列只会包含filter()
返回true的元素。
for i in (0..21).filter(|x| x % 2 == 0) {
print!("{} ", i);
}
上面的函数将会返回所有 0 - 20 的偶数
rev()
由于序列都是自增的,如果需要递减序列,可以使用rev() 反转一个序列
for i in (0..11).rev() {
print!("{} ", i);
}
上面的函数将会输出 10 - 0 的递减序列
map()
map()
同样接受一个闭包作为参数,它将闭包应用于序列中所有的元素,并返回处理结果
for i in (1..10).map(|x| x * x) {
print!("{} ", i);
}
fold()
fold()
就是其他语言的reduce()
它返回一个累加器,最终返回的是一个结果
let result = (1..=5).fold(0, |acc ,x| acc + x * x);
println!("result = {}", result);
上面的实现可以和下面的代码相同
let mut acc = 0;
for x in 1..=5 {
acc += x * x;
}
let result = acc;
println!("result = {}", result);
迭代数组
和迭代Range
相同,我们同样可以迭代一个数组,由于数组并不是迭代器类型,所以可以使用iter()方法将其转化为一个迭代器。
let cities = ["Toronto", "New York", "Melbourne"];
for city in cities.iter() {
print!("{}, ", city);
}
组合使用迭代函数
在上面的例子中我们都是单独使用每个迭代函数,但是迭代器最大的优势就是可以组合使用,例如
for i in (0..=10).rev().filter(|x| x % 2 == 0) {
print!("{} ", i);
}
上面的例子就会返回 10 - 0 所有的偶数。
如果需要一个非连续的序列,可以使用chain() 函数
let c = (1..4).chain(6..9);
for i in c {
print!("{} ", i);
}
上面的示例 会打印 1 2 3 6 7 8
还有一个函数是zip()
这个函数可以将两个迭代器组合在一起一一对应例如
let cities = ["Toronto", "New York", "Melbourne"];
let populations = [2_615_060, 8_550_405, 4_529_500];
let matrix = cities.iter().zip(populations.iter())
for (c, p) in matrix {
println!("{:10}: population = {}", c, p);
}
输出
// output:
// Toronto : population = 2615060
// New York : population = 8550405
// Melbourne : population = 4529500
迭代字符
对于字符的迭代访问可以使用char_iter
库
[dependencies]
char-iter = "0.1"
然后就可以使用
extern crate char_iter;
for c in char_iter::new('Д', 'П') {
print!("{} ", c);
}
迭代Vector
vector是Rust的基础结构之一,是一个动态数组,大多数情况下操作和数组相同,比如需要遍历vector。
let nums = vec![1,2,3,4,5];
for i in nums.iter() {
print!("{} ", i);
}
也可以直接如下:
let nums = vec![1, 2, 3, 4, 5];
for i in &nums {
print!("{} ", i);
}
注意,上面的例子是不可变借用,如果需要改变nums的内容需要使用可变借用
let mut nums = vec![1, 2, 3, 4, 5];
for i in &mut nums {
*i *= 2;
}
println!("{:?}", nums);
当然,上面这种方式是不推荐,可以直接使用map() 处理
let nums = vec![1,2,3,4,5];
let nums = nums.iter().map(|x| x * 2).collect::<Vec<i32>>();
collect() 的作用是接受迭代器返回的值,然后将其收集转化为需要的类型集合。例如需要1 - 10 的Vec<i32>
类型.
let v = (1..11).collect::<Vec<i32>>();
如果需要获取vector的索引和值,可以使用enumearte() 函数
let v = vec![1,2,3];
for (i, n) in vec.iter().enumerate() {
println!("v[{}] = {}", i, n);
}
// output:
// v[0] = 1
// v[1] = 2
// v[2] = 3
一些其他有用的函数例如,max(), min(),sum() 等
let v = vec![1,2,3];
let max = v.iter().max();
let min = v.iter().min();
println!("max = {:?}, min = {:?}", max, min);
// output: max = Some(5), min = Some(-2)
let grades = vec![4, 5, 6, 9, 7, 4, 8];
let sum: i32 = grades.iter().sum();
let gpa = sum as f32 / grades.len() as f32;
println!("sum = {}, gpa = {:.2}", sum, gpa);
// output: sum = 43, gpa = 6.14
itertools
itertools 库提供了非常多有用的迭代方法
Cargo.toml
[dependencies]
itertools = "0.6"
比如如果要处理上面使用filter() 获取所有偶数的例子,使用itertools 可以使用step() 实现
extern crate itertools;
use itertools::Itertools;
for i in (0..11).step_by(2) {
print!("{} ", i);
}
unique() 函数可以进行去重
extern crate itertools;
use itertools::Itertools;
let data = vec![1,4,5,1,4,5];
let unique = data.iter().unique();
for d in unique {
print!("{} ", d);
}
join() 函数可以将序列通过分隔符链接起来
extern crate itertools;
use itertools::Itertools;
let creatures = vec!["banshee", "basilisk", "centaur"];
let list = creatures.iter().join(", ");
println!("In the enchanted forest, we found {}.", list);
// output: In the enchanted forest, we found banshee, basilisk, centaur.
sorted_by() 进行排序
extern crate itertools;
use itertools::Itertools;
let happiness_index = vec![
("Canada", 7), ("Iceland", 4), ("Netherlands", 6),
("Finland", 1), ("New Zealand", 8), ("Denmark", 3),
("Norway", 2), ("Sweden", 9), ("Switzerland", 5)
];
let top_contries = happiness_index
.into_iter()
.sorted_by(|a, b| (&a.1).cmp(&b.1))
.into_iter()
.take(5);
for (country, rating) in top_contries {
println!("# {}: {}", rating, country);
}
// output:
// # 1: Denmark
// # 2: Switzerland
// # 3: Iceland
// # 4: Norway
// # 5: Finland
自己编写迭代器
我们将会编写一个简单的迭代器,他的作用是输出一堆温度表示(华氏,摄氏),类型是(f32, f32)。 他们的计算公式是 c = (f - 32) / 1.8
首先定义一个struct
struct FahrToCel {
fahr: f32,
step: f32,
}
impl FahrToCel {
fn new(fahr: f32, step: f32) -> FahrToCelc {
FahrToCelc { fahr: fahr, step: step }
}
}
下面为FahrToCel 实现Iterator trait
impl Iterator for FahrToCel {
type Item = (f32, f32);
fn next(&mut self) -> Option<Self::Item> {
let curr_fahr = self.fahr;
let curr_celc = (self.fahr - 32.0) / 1.8;
self.fahr = self.fahr + self.step;
Some((curr_fahr, curr_celc))
}
}
fn main() {
// pass the starting temperature and step to the initializer function
let ftc = FahrToCelc::new(0.0, 5.0);
// produce the iterator table of first 5 values
let temp_table = ftc.take(5);
// print out the temperature table nicely
for (f, c) in temp_table {
println!("{:7.2} °F = {:7.2} °C", f, c);
}
}