当前位置:网站首页>[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
边栏推荐
- [jailhouse article] performance measurements for hypervisors on embedded ARM processors
- Overview of variable resistors - structure, operation and different applications
- leetcode-6108:解密消息
- Daily question - longest substring without repeated characters
- Arduino 控制的 RGB LED 无限镜
- Sword finger offer 05 Replace spaces
- Annotation and reflection
- The connection and solution between the shortest Hamilton path and the traveling salesman problem
- Codeforces Round #716 (Div. 2) D. Cut and Stick
- Appium基础 — 使用Appium的第一个Demo
猜你喜欢

API related to TCP connection
![[cloud native] record of feign custom configuration of microservices](/img/39/05cf7673155954c90e75a8a2eecd96.jpg)
[cloud native] record of feign custom configuration of microservices

leetcode-6111:螺旋矩阵 IV

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

Spark中groupByKey() 和 reduceByKey() 和combineByKey()

Wazuh開源主機安全解决方案的簡介與使用體驗

Appium自动化测试基础 — Appium测试环境搭建总结

MIT-6874-Deep Learning in the Life Sciences Week 7

Light a light with stm32

【Jailhouse 文章】Jailhouse Hypervisor
随机推荐
Daily question - longest substring without repeated characters
Arduino 控制的 RGB LED 无限镜
LeetCode 0108.将有序数组转换为二叉搜索树 - 数组中值为根,中值左右分别为左右子树
Daily question 1342 Number of operations to change the number to 0
Configuration and startup of kubedm series-02-kubelet
Codeforces round 712 (Div. 2) d. 3-coloring (construction)
CPU内核和逻辑处理器的区别
【云原生】微服务之Feign自定义配置的记录
wordpress切换页面,域名变回了IP地址
leetcode-6108:解密消息
从Dijkstra的图灵奖演讲论科技创业者特点
2022 极术通讯-Arm 虚拟硬件加速物联网软件开发
【Rust 笔记】15-字符串与文本(上)
R语言【数据集的导入导出】
LaMDA 不可能觉醒吗?
Personal developed penetration testing tool Satania v1.2 update
shared_ Repeated release heap object of PTR hidden danger
1041 Be Unique
Simply sort out the types of sockets
Some common problems in the assessment of network engineers: WLAN, BGP, switch