rust迭代器

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的模块学习,要提升到函数式范式的层次来学习,这是快速掌握一门新语言的诀窍。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容