rust迭代器

2024-05-18  本文已影响0人  Wu杰语

1. 函数式

rust迭代器是函数式范式实现的主体,回想一下函数式语言的几个关键特征:

可以和rust实现函数式对应一下。

学习rust函数式,要和Java8进行类比对比,两者实现有异曲同工。

2. rust迭代器

2.1 Iterator和IntoIterator

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    …… // 很多默认方法
}
trait IntoIterator where Self::IntoIter: Iterator<Item=Self::Item> {
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

Iterator和IntoIterator是迭代器的构造方法,要实现自己的迭代器,只要实现Iterator方法。例如vec的迭代器创建

let v = vec![4, 20, 12, 8, 6];
let mut iterator = v.iter();
let mut iterator1 = v.iter_mut();

iter方法创建一个迭代器,将消耗vec中的值,iter_mut创建一个迭代器,迭代取vec中值的引用。

for element in &collection { ... }
for element in &mut collection { ... }
for element in collection { ... }

for语句会自动翻译为 (&collection).into_iter(), (&mut collection).into_iter, collection.into_iter();

2.2 迭代器惰性

2.2.1 filter和map

let text = "  ponies  \n   giraffes\niguanas  \nsquid".to_string();
let v: Vec<&str> = text.lines()
    .map(str::trim)
    .filter(|s| *s != "iguanas")
    .collect();
assert_eq!(v, ["ponies", "giraffes", "squid"]);

filter 会返回第三个迭代器,它只会从 map 迭代器的结果中生成闭包 |s| *s != "iguanas" 返回 true 的那些条目

2.2.2 filter_map和flat_map

fn filter_map<B, F>(self, f: F) -> impl Iterator<Item=B>
    where Self: Sized, F: FnMut(Self::Item) -> Option<B>;
use std::str::FromStr;

let text = "1\nfrond .25  289\n3.1415 estuary\n";
for number in text
    .split_whitespace()
    .filter_map(|w| f64::from_str(w).ok())
{
    println!("{:4.2}", number.sqrt());
}

fiter_map与filter的不同是返回值是Option。

fn flat_map<U, F>(self, f: F) -> impl Iterator<Item=U::Item>
    where F: FnMut(Self::Item) -> U, U: IntoIterator;

use std::collections::HashMap;

let mut major_cities = HashMap::new();
major_cities.insert("Japan", vec!["Tokyo", "Kyoto"]);
major_cities.insert("The United States", vec!["Portland", "Nashville"]);
major_cities.insert("Brazil", vec!["São Paulo", "Brasília"]);
major_cities.insert("Kenya", vec!["Nairobi", "Mombasa"]);
major_cities.insert("The Netherlands", vec!["Amsterdam", "Utrecht"]);

let countries = ["Japan", "Brazil", "Kenya"];

for &city in countries.iter().flat_map(|country| &major_cities[country]) {
    println!("{}", city);
}

flat_map取出vec,并将vec数组展平为一个数组

2.2.3 take

fn take(self, n: usize) -> impl Iterator<Item=Self::Item>
    where Self: Sized;

fn take_while<P>(self, predicate: P) -> impl Iterator<Item=Self::Item>
    where Self: Sized, P: FnMut(&Self::Item) -> bool;

let message = "To: jimb\r\n\
               From: superego <editor@oreilly.com>\r\n\
               \r\n\
               Did you get any writing done today?\r\n\
               When will you stop wasting time plotting fractals?\r\n";
for header in message.lines().take_while(|l| !l.is_empty()) {
    println!("{}" , header);
}

take取第0-n行数据,take_while取满足predicate的数据。

2.2.4 zip

use std::iter::repeat;

let endings = ["once", "twice", "chicken soup with rice"];
let rhyme: Vec<_> = repeat("going")
    .zip(endings)
    .collect();
assert_eq!(rhyme, vec![("going", "once"),
                       ("going", "twice"),
                       ("going", "chicken soup with rice")]);

zip将两个迭代器组合成一个迭代器

2.3 迭代器求值

如果没有求值函数,则迭代器就是惰性的,不会处理。

2.3.1 count、sum 和 product

use std::io::prelude::*;

fn main() {
    let stdin = std::io::stdin();
    println!("{}", stdin.lock().lines().count());
}

count计算迭代对象个数。

2.3.2 min和max

assert_eq!([-2, 0, 1, 0, -2, -5].iter().max(), Some(&1));
assert_eq!([-2, 0, 1, 0, -2, -5].iter().min(), Some(&-5));

min和max求最大和最小值

2.3.3 any和all

let id = "Iterator";

assert!( id.chars().any(char::is_uppercase));
assert!(!id.chars().all(char::is_uppercase));

any和all表达了任意和所有的语义。

2.3.4 fold

let a = [5, 6, 7, 8, 9, 10];

assert_eq!(a.iter().fold(0, |n, _| n+1), 6);        // 计数
assert_eq!(a.iter().fold(0, |n, i| n+i), 45);       // 求和
assert_eq!(a.iter().fold(1, |n, i| n*i), 151200);   // 乘积

// 最大值
assert_eq!(a.iter().cloned().fold(i32::min_value(), std::cmp::max),
           10);

fold实现累加器

2.3.5 collection

use std::collections::;

let args: HashSet<String> = std::env::args().collect();
let args: BTreeSet<String> = std::env::args().collect();
let args: LinkedList<String> = std::env::args().collect();

// 只有键–值对才能收集到Map中,因此对于这个例子,
// 要把字符串序列和整数序列拉合在一起
let args: HashMap<String, usize> = std::env::args().zip(0..).collect();
let args: BTreeMap<String, usize> = std::env::args().zip(0..).collect();

// 其他代码略

colleciton构建集合

2.3.5 for_each

["doves", "hens", "birds"].iter()
    .zip(["turtle", "french", "calling"])
    .zip(2..5)
    .rev()
    .map(|((item, kind), quantity)| {
        format!("{} {} {}", quantity, kind, item)
    })
    .for_each(|gift| {
        println!("You have received: {}", gift);
    });

for_each遍历迭代对象

小结

rust的迭代器,对照Java8,很容易轻车熟路。学习迭代器,不要仅仅把迭代器当做一个设计模式或者rust的模块学习,要提升到函数式范式的层次来学习,这是快速掌握一门新语言的诀窍。

上一篇 下一篇

猜你喜欢

热点阅读