当前位置:网站首页>[rust notes] 13 iterator (Part 2)
[rust notes] 13 iterator (Part 2)
2022-07-05 06:03:00 【phial03】
13.4 - Consumer iterators
13.4.1 - Simple accumulation
countMethod : Take values from an iterator , Until it returnsNone, Then tell you how many items this iterator contains .use std::io::prelude::*; fn main() { let stdin = std::io::stdin(); println!("{}", stdin.lock().lines().count()); }sumMethod : Used to calculate iterator items ( Must be an integer or floating point number ) And .productMethod : Used to calculate iterator items ( Must be an integer or floating point number ) Product of .use std::iter::{ Sum, Product}; fn triangle(n: u64) -> u64 { (1.. n+1).sum() } assert_eq!(triangle(20), 210); fn factorial(n: u64) -> u64 { (1.. n+1).product() } assert_eq!(factorial(20), 2432902008176640000);
13.4.2-max and min
IteratorSpecialmaxandminMethod returns the maximum and minimum values of the items generated by the iterator . The item type of the iterator must implementstd::cmp::Ord, In this way, items can be compared with each other .assert_eq!([-2, 0, 1, 0, -2, -5].iter().max(), Some(&1)); assert_eq!([-2, 0, 1, 0, -2, -5].iter().min(), Some(&-5));These two methods return
Option<Self::Item>, Therefore, when the iterator does not produce an item, it returnsNone.Floating point type
f32andf64onlystd::cmp::PartialOrd, It didn't come truestd::cmp::Ord, Therefore, the above two methods cannot be used .
13.4.3-max_by and min_by
max_byandmin_byMethod according to the custom comparison method provided , Returns the maximum and minimum values generated by the iterator .use std::cmp::{ PartialOrd, Ordering}; // Compare the two f64 Value , If you include NAN Is surprised fn cmp(lhs: &&f64, rhs: &&f64) -> Ordering { lhs.partial_cmp(rhs).unwrap() } let numbers = [1.0, 4.0, 2.0]; // Sequence does not contain NAN Comparison assert_eq!(numbers.iter().max_by(cmp), Some(&4.0)); assert_eq!(numbers.iter().min_by(cmp), Some(&1.0)); let numbers = [1.0, 4.0, std::f64::NAN, 2.0]; // The sequence contains NAN Comparison assert_eq!(numbers.iter().max_by(cmp), Some(&4.0)); // There will be surprise during the comparison&&f64Use double quotation , Becausenumbers.iter()Will generate a reference to the element , thenmax_byandmin_byPass the reference of iterator item to closure .
13.4.4-max_by_key and min_by_key
IteratorOfmax_by_keyandmin_by_keyMethods can be based on the closure applied to each item , Select the largest or smallest item ./// Take each item as a parameter for a given , And return the ordered type B The closure of , /// They return to The closure returns B Maximum or minimum The item . /// If no item is generated , Then return to None. fn max_by_key<B: Ord, F>(self, f: F) -> Option<Self:: Item> where Self: Sized, F: FnMut(&Self::Item) -> B; fn min_by_key<B: Ord, F>(self, f: F) -> Option<Self:: Item> where Self: Sized, F: FnMut(&Self::Item) -> B;- Closures can select a field of an item , Or perform some calculations for each item .
- Just care about the data associated with the largest or smallest item .
give an example : From the hash table of a city, find the city with the largest and smallest population :
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)));- Closure
|&(_name, pop)| popWill be applied to every item produced by the iterator , The returned value is used for comparison . - The population returned here . The returned value is the entire item , Instead of the value returned by the closure .
- Closure
13.4.5 - Comparison item sequence
For strings 、 Vectors and slices , have access to
>、<and==Operator comparison .For iterators , have access to
eqandltAnd other methods to achieve the same function . Get the paired items from the iterator , And then compare them , Until a decision can be made :eqandneMethod is used for equality comparison ;lt、le、gtandgeMethod is used for order comparison .cmpandpartial_cmpMethod , AndOrdandPartialOrdThe corresponding method on the special type is similar .
let packed = "Helen of Troy"; let spaced = "Helen of Troy"; let obscure = "Helen of Sandusky"; assert_eq!(packed != spaced); assert_eq!(packed.split_whitespace().eq(spaced.split_whitespace())); // return true, because ' ' < 'o' assert!(spaced < obscure); // return true, because 'Troy' > 'Sandusky' assert!(spaced.split_whitespace().gt(obscure.split_whitespace()));- This is called
split_whitespace()Method , You can return the iterator to generate the word obtained by dividing the string with spaces . - Make the call
eqandgtMethod , Can perform word to word comparisons , Instead of comparing characters . - because
&strRealizedPartialOrdandPartialEqSpecial type .
13.4.6-any and all
anyMethod : Give each application closure generated by the iterator , If the closure returnstrue, Then the method returnstrue.allMethod : Give each application closure generated by the iterator , If the closure returns for all itemstrue, Then the method returnstruelet id = "Iterator"; assert!(id.chars().any(char::is_uppercase)); assert!(!id.chars().all(char::is_uppercase));
13.4.7-position、rposition and ExactSizeIterator
positionMethod : Give each application closure generated by the iterator , The return value is oneOptionType value :If the closure does not return any items
true, Then the method returnsNone.As long as the closure returns
true,positionStop taking values , And return the index of the item .let text = "Xerxes"; assert_eq!(text.chars().position(|c| c == 'e'), Some(1)); assert_eq!(text.chars().position(|c| c == 'z'), None);
rpositionMethod : Function andpositionidentical , Just search from the right .The iterator required is a reversible iterator , In this way, the value can be taken from the right end of the iterator .
The iterator size is required to be fixed , In this way, it can be like
positionThen assign values to the index , Also take the leftmost item as 0.Cannot use... On a string
rposition. Use... On stringsbMark , Make the string convert to byte array .let bytes = b"Xerxes"; assert_eq!(bytes.iter().rposition(|&c| c == b'e'), Some(4)); assert_eq!(bytes.iter().rposition(|&c| c == b'X'), Some(0));
Fixed size iterators , It means it's done
std::iter::ExactSizeIteratorSpecial iterators :pub trait ExactSizeIterator: Iterator { fn len(&self) -> usize { ... } fn is_empty(&self) -> bool { ... } }lenMethod returns the number of remaining items .is_emptyMethod returnstrue.
13.4.8-fold
foldMethod : Perform a cumulative operation on the entire sequence of items generated by the iterator .- Receive an accumulator (accumulator) And a closure ,
- Then apply the closure repeatedly to the next item of the current accumulator and iterator .
- The return value of each closure will become the new value of the accumulator , And then with the next item of the iterator , Pass it to the closure .
- Return value : The final value of the accumulator is also
foldReturn value of method . If the sequence is empty , befoldReturns the initial value of the accumulator .
foldMethod signature :fn fold<A, F>(self, init: A, f: F) -> A where Self: Sized, F: FnMut(A, Self::Item) -> A;AIt's the type of accumulator .initThe parameter is one A, I.e. accumulator .- The first parameter and return value of the closure are
A.
Many other methods of iterators can be written using
foldIn the form of :let a = [5, 6, 7, 8, 9, 10]; assert_eq!(a.iter().fold(0, |n, _| n + 1), 6); // Realization count assert_eq!(a.iter().fold(0, |n, i| n + i), 45); // Realization sum assert_eq!(a.iter().fold(1, |n, i| n * i), 151200); // Realization product assert_eq!(a.iter().fold(i32::min_value(), |m, &i| std::cmp::max(m, i)), 10);// Realization maxThe value of the accumulator will be transferred to the closure , Then transfer it out . Therefore, we can be right and wrong
CopyType accumulator usesfoldMethod :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, "Pach my box with five dozen liquor jugs");
13.4.9-nth
nthMethod : Receive an index valuen, Then skip the corresponding item in the iterator , Return the item corresponding to the index .If the sequence ends before , Then return to
None.call
.nth(0)Equivalent to calling.next().fn nth(&mut self, n: usize) -> Option<Self::Item> where Self: Sized;
nthWill not take ownership of the iterator , Therefore, you can call :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);
13.4.10-last
lastMethod : Use closures to consume each item of the iterator , Until the closure returnsNone, Then return to the last item . If the iterator does not produce any items , belastMethod will returnNone.fn last(self) -> Option<Self::Item>;give an example :
let squares = (0..10).map(|i| i * i); assert_eq!(squares.last(), Some(81));If the iterator is reversible , It doesn't need to consume all its items , You can use
iter.rev().next()Instead of realizing .
13.4.11-find
findMethod : Get the first return from the iteratortrueValue , Or if no suitable item is found, returnNone.fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where Self: Sized, P: FnMut(&Self:: Item) -> bool;give an example :
assert_eq!(populations.iter().find(|&(_name, &pop)| pop > 1_000_000), None); assert_eq!(populations.iter().find(|&(_name, &pop)| pop > 500_000), Some((&"Portland", &583_776)));
13.4.12 - Build collections
collectMethod : Build a collection that holds iterator items .let args: Vec<String> = std::env::args().collect(); // Construct vector use std::collections::{ HashSet, BtreeSet, LinkedList, HashMap, BTreeMap}; // Build any other collection let args: HashSet<String> = std::env::args().collect(); let args: BTreeSet<String> = std::env::args().collect(); let args: LinkedList<String> = std::env::args().collect(); // Aggregation mapping requires (key,value) Yes , So for this example , It can be used zip Add an integer index to the string let args: HashMap<String, usize> = std::env::args().zip(0..).collect(); let args: BTreeMap<String, usize> = std::env::args().zip(0..).collect();collectThe return type of is its type parameter , So the aboveVecandHashSetThe call of is equivalent to :let args = std::env::args().collect::<Vec<String>>(); let args = std::env::args().collect::<HashSet<String>>();
FromIteratorMethod :collectI don't know how to build the above set type .contrary
VecorHashMapAnd other collection types know how to build themselves based on iterators , Because they achievestd::iter::FromIteratorSpecial type .trait FromIterator<A>: Sized { fn from_iter<T: IntoIterator<Item=A>>(iter: T) -> Self }If a collection type implements
FromIterator<A>, Then its static methodfrom_iterBased on the generation typeAThe iteratable type of builds the value of that type .
IteratorSpecialsize_hintMethod : Returns the lower limit and optional upper limit of the number of items generated by the iterator .- The default definition returns 0 As the lower limit , No upper limit is given .
- It can make
VecYesFromIteratorThe implementation of the , Provide the necessary information for allocating the correct size of the new vector buffer . HashSetandHashMapYou can also useIterator::size_hintCreate an appropriate initial size for your hash table .
trait Iterator{ ... fn size_hint(&self) -> (usize, Option<usize>) { (0, None) } }
13.4.13-Extend Special type
All standard library collections are implemented
std::iter::ExtendSpecial type :trait Extend<A> { fn extend<T>(&mut self, iter: T) where T: IntoIterator<Item=A>; }extend()Method can add the item of an iterator to the collection :let mut v: Vec<i32> = (0..5).map(|i| 1 << i).collect(); v.extend(&[31, 57, 99, 163]); assert_eq!(v, &[1, 2, 4, 8, 16, 31, 57, 99, 163]);Fixed length arrays and slices do not support this method .
FromIteratorUsed to create a new set , andExtendUsed to extend existing collections .Some in the standard library
FromIteratorRealization , Is to create an empty set first , And then useextendMethod to populate this collection .for example
std::collections::LinkedListThe implementation of the :impl<T> FromIterator<T> for LinkedList<T> { fn from_iter<I: IntoIterator<Item=T>>(iter: I) -> Self { let mut list = Self::new(); list.extend(iter); list } }
13.4.14-partition
partitionMethod : Divide the items of an iterator into two sets , Then use closures to decide which item belongs to which set .partitionYou can generate any two collections of the same type .partitionReturn type... Must be specified , Let type inference choose to call the correct type parameters .fn partition<B, F>(self, f: F) -> (B, B) where Self: Sized, B: Default + Extend<Self::Item>, F: FnMut(&Self::Item) -> bool;collectThe result type is required to realizeFromIterator;partitionThe result type is required to realizestd::default::Defaultandstd::default::Extend.all Rust A collection of
std::default::DefaultAll implementations return an empty set .
give an example :
let things = ["doorknob", "mushroom", "noodle", "giraffe", "grapefruit"]; // Find out that the first letter is n Letters ,n It's odd . let (living, nonliving): (Vec<&str>, Vec<&str>) = things.iter().partition(|name| name.as_bytes()[0] & 1 != 0); assert_eq!(living, vec!["mushroom", "giraffe", "grapefruit"]); assert_eq!(nonliving, vec!["doorknob", "noodle"]);Cut an iterator into two , Two parts need to share the same underlying iterator , Iterators must be modifiable to use . The underlying iterators must be shared and modifiable , This is right Rust It is against the safety rules .
- therefore ,
partitionMethod divides the iterator into two sets , Instead of two iterators . - Take it out of the underlying iterator ( But it has not been taken out of the segmented iterator ) Value , It needs to be cached in the set first .
- therefore ,
13.5 - Implement iterators for custom types
Custom types can also be implemented
IntoIteratorandIteratorSpecial type :- Support all adapters and consumers mentioned above .
- It also supports third-party libraries that use standard iterator interfaces .
give an example 1: Implement a simple iterator based on a range type .
Define a scope type (
std::ops::Range<T>Simplified version of type ):// iteration I32Range Two states are required : // ===> Current value : Use start As the next value . // ===> Constraints for ending iterations : Use end As a condition of limitation . struct I32Range { start: i32, end: i32 }Realization
IteratorSpecial type :impl Iterator for I32Range { type Item = i32; fn next(&mut self) -> Option<i32> { if self.start >= self.end { return None; // The end condition } let result = Some(self.start); self.start += 1; result // Return value } }The standard library implements
IteratorThe type of , All provide correspondingIntoIteratorGeneral implementation of , thereforeI32RangeCan be used directly :let mut pi = 0.0; mit mut numerator = 1.0; for k in (I32Range { start: 0, end: 14 }) { pi += numerator / (2*k + 1) as f64; numerator /= -3.0 } pi *= f64::sqrt(12.0); // IEEE 754 Clearly defined PI assert_eq!(pi as f32, std::f32::consts::PI);I32RangeThe iteratable type and iterator of are the same type .
give an example 2: Implement a more complex iterator based on a binary tree type .
Create a binary tree type :
enum BinaryTree<T> { Empty, NonEmpty(Box<TreeNode<T>>) } struct TreeBode<T> { element: T, left: BinaryTree<T>, right: BinaryTree<T> }The classic way to traverse a binary tree is to use recursion : Through the function call stack , Track the location in the number and the nodes that have not been visited .
For
BinaryTree<T>RealizationIteratorwhen , Every time you callnextYou must generate and return a value . For tracking tree nodes that have not yet been generated , Iterators must maintain their own stack .in the light of
BinaryTreeImplement an iterator type :use self::BinaryTree::*; // Yes BinaryTree Perform sequential iterations struct TreeIter<'a, T: 'a> { // Stack of tree node applications . // Because use Vec Of push and pop Method , So the top of the stack is the end of the vector . // // The node iterator will get the next value from the top of the stack , The unreached ancestor nodes are below . // If the stack is empty , End of iteration unvisited: Vec<&'a TreeNode<T>> }A common operation is to define an auxiliary method : Push the node on the left of the subtree to the stack :
impl<'a, T: 'a> TreeIter<'a, T> { fn push_left_edge(&mut self, mut tree: &'a BinaryTree<T>) { while let NonEmpty(ref node) = *tree { self.unvisited.push(node); tree = &node.left; } } }Next, you can
BinaryTreeDefinitioniterMethod , Returns the iterator of the tree :impl<T> BinaryTree<T> { // Build an empty TreeIter, And then call push_left_edge To fill the initial stack . // according to unvisited The rules of the stack , The leftmost node is above . fn iter(&self) -> TreeIter<T> { let mut iter = TreeIter { unvisited: Vec::new() }; iter.push_left_edge(self); iter } }Implementation of reference standard library , Implement on a shared reference of the tree
IntoIterator, You can callBinaryTree::iter, Returns an iterator :impl<'a, T: 'a> IntoIterator for &'a BinaryTree<T> { type Item = &'a T; type IntoIter = TreeIter<'a, T>; // take TreeIter As &BinaryTree The iterator type of fn into_iter(self) -> Self::IntoIter { self.iter() } }Iterator
nextMethods also act according to the rules of the stack :impl<'a, T> Iterator for TreeIter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { // Find the nodes that the iteration must produce , Or end the iteration . let node = match self.unvisited.pop() { None => return None; Some(n) => n }; // If the node has a right subtree , The next node after the current node , It is the leftmost child node of the right child node of the current node // Push it into the stack self.push_left_edge(&node.right); // Generate a reference to the node value Some(&node.element) // If the stack is empty , End of iteration . // otherwise ,node Is the reference to the current node , And the call will return to its element References to fields . } }You can use
forLoop iterates by referenceBinaryTree:fn make_node<T>(left: BinaryTree<T>, element: T, right: BinaryTree<T>) -> BinaryTree<T> { NonExpty(Box::new(TreeNode { left, element, right })) } fn main() { // Build a tree let subtree_l = make_node(Empty, "mecha", Empty); let subtree_rl = make_node(Empty, "droid", Empty); let subtree_r = make_node(subtree_l, "robot", Empty); let tree = make_node(subtree_l, "Jaeger", subtree_r); // Iterative tree let mut v = Vec::new(); for kind in &tree { v.push(*kind); } assert_eq!(v, ["mecha", "Jaeger", "droid", "robot"]); }All commonly used iterator adapters and consumers , Can be used on this tree :
assert_eq!( tree.iter() .map(|name| format!("mega-{}", name)) .collect::<Vec<_>>(), vec!["mega-mecha", "mega-Jaeger", "mega-droid", "mega-robot"] );
See 《Rust Programming 》( Jim - Brandy 、 Jason, - By orendov , Translated by lisongfeng ) Chapter 15
Original address
边栏推荐
- 2020ccpc Qinhuangdao J - Kingdom's power
- 1.15 - 输入输出系统
- Codeforces Round #732 (Div. 2) D. AquaMoon and Chess
- Dynamic planning solution ideas and summary (30000 words)
- leetcode-6110:网格图中递增路径的数目
- Introduction to LVS [unfinished (semi-finished products)]
- 【Rust 笔记】13-迭代器(下)
- Over fitting and regularization
- Dichotomy, discretization, etc
- F - Two Exam(AtCoder Beginner Contest 238)
猜你喜欢

QQ computer version cancels escape character input expression

可变电阻器概述——结构、工作和不同应用

redis发布订阅命令行实现

LeetCode 0107.二叉树的层序遍历II - 另一种方法

leetcode-6108:解密消息

7. Processing the input of multidimensional features

从Dijkstra的图灵奖演讲论科技创业者特点

Typical use cases for knapsacks, queues, and stacks

leetcode-6110:网格图中递增路径的数目

【Jailhouse 文章】Jailhouse Hypervisor
随机推荐
leetcode-6111:螺旋矩阵 IV
Sword finger offer 53 - I. find the number I in the sorted array
EOJ 2021.10 E. XOR tree
【Jailhouse 文章】Jailhouse Hypervisor
数据可视化图表总结(一)
leetcode-6110:网格图中递增路径的数目
Educational Codeforces Round 107 (Rated for Div. 2) E. Colorings and Dominoes
QQ computer version cancels escape character input expression
Daily question 1342 Number of operations to change the number to 0
2022年貴州省職業院校技能大賽中職組網絡安全賽項規程
QQ电脑版取消转义符输入表情
How many checks does kubedm series-01-preflight have
Smart construction site "hydropower energy consumption online monitoring system"
How to adjust bugs in general projects ----- take you through the whole process by hand
【云原生】微服务之Feign自定义配置的记录
Alu logic operation unit
js快速将json数据转换为url参数
Common optimization methods
Daily question 2006 Number of pairs whose absolute value of difference is k
剑指 Offer II 058:日程表