1. 函数式
rust迭代器是函数式范式实现的主体,回想一下函数式语言的几个关键特征:
- 高阶函数:函数可以作为参数传递给其他函数,也可以作为返回值。这允许函数式程序员编写更加模块化和可重用的代码。
- 惰性求值:函数式编程中的表达式只有在需要时才进行求值,这可以帮助提高程序的效率。
- 函数组合:函数可以被组合在一起形成新的函数,这可以使代码更加模块化和可重用。
- 类型系统和类型推断:函数式编程语言通常具有强大的类型系统,这可以帮助减少运行时错误和提高代码的安全性。
可以和rust实现函数式对应一下。
- 类型系统和类型推断 -> rust的patten,match =>
- 高阶函数 -> rust函数作为返回值,闭包
- 函数组合 -> 迭代器map/filter..
- 惰性求值 -> 迭代器collect/foreach..
学习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();
- (&collection).into_iter() 会返回一个 Item 类型为 &T 的迭代器。该迭代器会生成对其条目的共享引用
- (&mut collection).into_iter() 会返回一个 Item 类型为 &mut T的迭代器。给定对集合的可变引用,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的模块学习,要提升到函数式范式的层次来学习,这是快速掌握一门新语言的诀窍。