Rust 的迭代器設計精妙,不僅能有效處理集合元素,還能透過介面卡串接不同的操作。by_ref 允許在不消耗原始迭代器的情況下,借用其一部分進行操作,例如處理郵件標頭後繼續處理內文。cloned 介面卡則能方便地複製迭代器產生的值,避免繁瑣的複製邏輯。cycle 介面卡讓迭代器無限迴圈,適用於迴圈播放等場景。此外,Rust 還提供 count、sum、product 等方法,能快速計算迭代器元素的數量、總和與乘積。max 和 min 方法則能找出迭代器中的最大值和最小值,搭配自定義比較函式,更能處理複雜的比較邏輯,例如浮點數比較。這些方法讓 Rust 迭代器在處理集合時更加靈活高效。
迭代器介面卡與消耗方法
在 Rust 程式語言中,迭代器是一種強大的工具,用於處理集合中的元素。本章節將探討迭代器的進階用法,包括 by_ref、cloned、cycle 等介面卡,以及如何消耗迭代器。
使用 by_ref 保留原始迭代器
by_ref 方法會借用一個可變的迭代器參考,讓你可以在這個參考上套用其他介面卡。當這些介面卡被丟棄後,借用結束,你可以重新取回原始迭代器的控制權。
例子:處理郵件訊息的標頭和內文
let message = "To: jimb\r\nFrom: id\r\n\r\nOooooh, donuts!!\r\n";
let mut lines = message.lines();
println!("Headers:");
for header in lines.by_ref().take_while(|l| !l.is_empty()) {
println!("{}", header);
}
println!("\nBody:");
for body in lines {
println!("{}", body);
}
內容解密:
lines.by_ref()借用lines迭代器的可變參考。take_while介面卡被套用到這個可變參考上,用於處理郵件標頭。- 當
take_while結束後,可變參考被釋放,原始的lines迭代器可以繼續使用。
cloned 介面卡:複製參考值
cloned 介面卡用於將產生參考值的迭代器轉換為產生複製值的迭代器。被參考的值必須實作 Clone 特徵。
例子:複製陣列元素
let a = ['1', '2', '3', '∞'];
assert_eq!(a.iter().next(), Some(&'1'));
assert_eq!(a.iter().cloned().next(), Some('1'));
內容解密:
a.iter()產生對陣列元素的參考。cloned()將這些參考轉換為複製的值。
cycle 介面卡:無限迴圈迭代
cycle 介面卡使迭代器無限迴圈產生元素。底層迭代器必須實作 Clone,以便在每次迴圈開始時重置狀態。
例子:無限迴圈方向
let dirs = ["North", "East", "South", "West"];
let mut spin = dirs.iter().cycle();
assert_eq!(spin.next(), Some(&"North"));
assert_eq!(spin.next(), Some(&"East"));
assert_eq!(spin.next(), Some(&"South"));
assert_eq!(spin.next(), Some(&"West"));
assert_eq!(spin.next(), Some(&"North"));
內容解密:
dirs.iter().cycle()建立一個無限迴圈的迭代器。- 每次呼叫
next(),迭代器產生下一個方向名稱,無限迴圈。
簡單累積:count、sum、product
count方法計算迭代器產生的元素數量。sum和product方法分別計算元素的總和與乘積。
例子:計算標準輸入的行數
use std::io::prelude::*;
fn main() {
let stdin = std::io::stdin();
println!("{}", stdin.lock().lines().count());
}
內容解密:
stdin.lock().lines()建立一個迭代器,用於讀取標準輸入的行。count()方法計算行數。
最大值與最小值:max、min
max 和 min 方法傳回迭代器產生的最大值和最小值。元素型別必須實作 Ord 特徵。
例子:找出一系列數字的最大值和最小值
assert_eq!([-2, 0, 1, 0, -2, -5].iter().max(), Some(&1));
assert_eq!([-2, 0, 1, 0, -2, -5].iter().min(), Some(&-5));
內容解密:
iter()方法產生對陣列元素的參考。max()和min()方法找出這些參考中的最大值和最小值。
使用自定義比較函式:max_by、min_by
當元素型別沒有實作 Ord(如浮點數),可以使用 max_by 和 min_by 方法,並提供自定義的比較函式。
例子:比較浮點數
use std::cmp::{PartialOrd, Ordering};
fn cmp(lhs: &&f64, rhs: &&f64) -> Ordering {
lhs.partial_cmp(rhs).unwrap()
}
let numbers = [1.0, 4.0, 2.0];
assert_eq!(numbers.iter().max_by(cmp), Some(&4.0));
內容解密:
- 自定義比較函式
cmp用於比較浮點數參考。 max_by(cmp)使用此函式找出最大值。
深入理解 Rust 中的迭代器方法
Rust 的迭代器提供了一系列強大的方法,用於處理集合中的元素。本文將重點介紹 max_by_key、min_by_key、any、all、position、rposition 和 fold 等方法,並透過例項演示其用法。
使用 max_by_key 和 min_by_key 查詢極值
max_by_key 和 min_by_key 方法允許你根據某個閉包傳回的值來比較迭代器中的元素,從而找到最大值或最小值的元素。這兩個方法的簽名如下:
fn min_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
where Self: Sized, F: FnMut(&Self::Item) -> B;
fn max_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
where Self: Sized, F: FnMut(&Self::Item) -> B;
例項:查詢城市人口最多和最少
use std::collections::HashMap;
let mut populations = HashMap::new();
populations.insert("Portland", 583_776);
populations.insert("Fossil", 449);
populations.insert("Greenhorn", 2);
populations.insert("Boring", 7_762);
populations.insert("The Dalles", 15_340);
assert_eq!(populations.iter().max_by_key(|&(_name, pop)| pop),
Some((&"Portland", &583_776)));
assert_eq!(populations.iter().min_by_key(|&(_name, pop)| pop),
Some((&"Greenhorn", &2)));
內容解密:
- 我們首先建立了一個
HashMap來儲存城市及其人口。 - 使用
iter()方法遍歷HashMap,傳回一個迭代器。 max_by_key和min_by_key方法根據閉包傳回的人口數來比較元素。- 閉包
|&(_name, pop)| pop提取每個元素的第二個值(人口數)進行比較。 - 最終傳回包含最大或最小人口的城市及其人口的元組。
比較專案序列
Rust 中的迭代器提供了多種方法來比較序列,如 eq、lt、gt 等。這些方法可以對兩個迭代器中的元素進行逐一比較。
例項:比較字串中的單詞
let packed = "Helen of Troy";
let spaced = "Helen of Troy";
let obscure = "Helen of Sandusky";
assert!(packed.split_whitespace().eq(spaced.split_whitespace()));
assert!(spaced.split_whitespace().gt(obscure.split_whitespace()));
內容解密:
- 使用
split_whitespace()方法將字串分割成單詞迭代器。 eq方法比較兩個迭代器中的元素是否完全相同。gt方法比較兩個迭代器中的元素,按照字典順序判斷前者是否大於後者。
使用 any 和 all 檢查條件
any 和 all 方法用於檢查迭代器中的元素是否滿足某個條件。
例項:檢查字串中是否包含大寫字母
let id = "Iterator";
assert!(id.chars().any(char::is_uppercase));
assert!(!id.chars().all(char::is_uppercase));
內容解密:
any方法檢查迭代器中是否有任何元素滿足閉包中的條件(在這裡是大寫字母)。all方法檢查迭代器中的所有元素是否都滿足閉包中的條件。
使用 position 和 rposition 查詢元素位置
position 和 rposition 方法用於查詢滿足某個條件的第一個或最後一個元素的位置。
例項:查詢字元在字串中的位置
let text = "Xerxes";
assert_eq!(text.chars().position(|c| c == 'e'), Some(1));
assert_eq!(text.chars().rposition(|c| c == 'e'), None); // 注意:chars() 傳回的迭代器不是 ExactSizeIterator
內容解密:
position方法從左到右查詢第一個滿足條件的元素,並傳回其索引。rposition方法從右到左查詢第一個滿足條件的元素,但需要迭代器實作ExactSizeIterator特徵。
使用 fold 累積結果
fold 方法是一種通用的累積結果的方法,可以用於實作多種操作,如計數、求和、求最大值等。
例項:使用 fold 進行各種累積操作
let a = [5, 6, 7, 8, 9, 10];
assert_eq!(a.iter().fold(0, |n, _| n + 1), 6); // count
assert_eq!(a.iter().fold(0, |n, i| n + i), 45); // sum
assert_eq!(a.iter().fold(1, |n, i| n * i), 151200); // product
assert_eq!(a.iter().fold(i32::min_value(), |m, &i| std::cmp::max(m, i)), 10); // max
內容解密:
fold方法接受一個初始值和一個閉包,閉包中累積結果並傳回新的累積值。- 可以用於計數(累加計數器)、求和(累加元素)、求乘積(累乘元素)等操作。
- 在查詢最大值時,初始值設為
i32::min_value(),確保任何元素都大於或等於它。
Rust 迭代器深入解析
Rust 的迭代器是一種強大的工具,用於處理集合中的元素。本篇文章將探討 Rust 迭代器的各種方法和應用。
使用 fold 方法累積值
fold 方法是一種強大的迭代器方法,用於將集合中的元素累積成一個值。它需要一個初始值和一個閉包,閉包的第一個引數是累積值,第二個引數是當前元素,傳回值是新的累積值。
let a = ["Pack ", "my ", "box ", "with ", "five ", "dozen ", "liquor ", "jugs"];
let pangram = a.iter().fold(String::new(), |mut s, &w| {
s.push_str(w);
s
});
assert_eq!(pangram, "Pack my box with five dozen liquor jugs");
內容解密:
fold方法的第一個引數String::new()是初始值,型別為String。- 閉包
|mut s, &w|中,s是累積值,w是當前元素。 s.push_str(w)將當前元素追加到累積值s中。- 傳回新的累積值
s。
使用 nth 方法取得指定索引的元素
nth 方法用於取得迭代器中指定索引的元素。如果索引超出範圍,則傳回 None。
let mut squares = (0..10).map(|i| i*i);
assert_eq!(squares.nth(4), Some(16));
assert_eq!(squares.nth(0), Some(25));
assert_eq!(squares.nth(6), None);
內容解密:
(0..10).map(|i| i*i)建立一個迭代器,生成 0 到 9 的平方數。squares.nth(4)取得第 4 個元素,即 16。squares.nth(0)取得下一個元素,即 25,因為之前的nth(4)已經消耗了前 4 個元素。squares.nth(6)傳回None,因為剩下的元素不足 6 個。
使用 last 方法取得最後一個元素
last 方法用於取得迭代器的最後一個元素。如果迭代器為空,則傳回 None。
let squares = (0..10).map(|i| i*i);
assert_eq!(squares.last(), Some(81));
內容解密:
(0..10).map(|i| i*i)建立一個迭代器,生成 0 到 9 的平方數。squares.last()取得最後一個元素,即 81。
使用 find 方法查詢元素
find 方法用於查詢迭代器中第一個滿足條件的元素。如果找不到,則傳回 None。
let populations = vec![("Portland", 583776)];
assert_eq!(populations.iter().find(|&(_name, &pop)| pop > 1000000), None);
assert_eq!(populations.iter().find(|&(_name, &pop)| pop > 500000), Some((&"Portland", &583776)));
內容解密:
populations.iter().find()在populations迭代器中查詢元素。- 第一個閉包
|&(_name, &pop)| pop > 1000000檢查人口是否大於 100 萬,結果為None。 - 第二個閉包
|&(_name, &pop)| pop > 500000檢查人口是否大於 50 萬,結果為Some((&"Portland", &583776))。
從迭代器構建集合:collect 和 FromIterator
使用 collect 方法構建集合
collect 方法用於將迭代器的元素收集到一個集合中。它可以構建各種型別的集合,如 Vec、HashSet、BTreeSet 等。
use std::collections::{HashSet, BTreeSet, LinkedList, HashMap, BTreeMap};
let args: Vec<String> = std::env::args().collect();
let args: HashSet<String> = std::env::args().collect();
let args: BTreeSet<String> = std::env::args().collect();
let args: LinkedList<String> = std::env::args().collect();
let args: HashMap<String, usize> = std::env::args().zip(0..).collect();
let args: BTreeMap<String, usize> = std::env::args().zip(0..).collect();
內容解密:
std::env::args()取得命令列引數的迭代器。collect()將迭代器的元素收集到不同的集合型別中。zip(0..)用於建立(key, value)對,以便構建HashMap和BTreeMap。
FromIterator 特徵
FromIterator 特徵定義瞭如何從迭代器構建集合。它包含一個方法 from_iter,用於從迭代器建立集合。
trait FromIterator<A>: Sized {
fn from_iter<T: IntoIterator<Item=A>>(iter: T) -> Self;
}
內容解密:
FromIterator<A>特徵用於從迭代器構建型別為A的集合。from_iter方法接受一個迭代器,並傳回一個集合。