404 #960
Replies: 12 comments 4 replies
-
|
Beta Was this translation helpful? Give feedback.
-
写的非常精彩醍醐灌顶 |
Beta Was this translation helpful? Give feedback.
-
这里讲的真的精彩 滑稽.png |
Beta Was this translation helpful? Give feedback.
-
看来作者还没写, 贴一个前一阵子看迭代器源码的笔记, 给大家做参考 Iterator TricksIterator 是一个特征, 用来实现迭代器. 这些特征很多常用库都会用到, 并且特质基本相同, 因此直接整理该特征是很有必要的. 迭代器定义关联1个类型: Item, 代指迭代器内元素的类型 pub trait Iterator {
/// The type of the elements being iterated over.
type Item; 迭代器方法注: 方法名带 &mut 类方法==可变迭代器==: 这类方法需要迭代器是可变的(
|
输出类型 | From | To |
---|---|---|
Box |
IntoIterator<Item = I> | Box<[I]> |
BinaryHeap |
IntoIterator<Item = T> | BinaryHeap<T> |
BTreeMap |
IntoIterator<Item = (K, V)> | BTreeMap<K, V> |
BTreeSet |
IntoIterator<Item = T> | BTreeSet<T> |
LinkedList |
IntoIterator<Item = T> | LinkedList<T> |
VecDeque |
IntoIterator<Item = T> | VecDeque<T> |
Rc |
IntoIterator<Item = T> | Rc<[T]> |
String |
IntoIterator<Item = char> | String |
IntoIterator<Item = &'a char> | String | |
IntoIterator<Item = &'a str> | String | |
IntoIterator<Item = String> | String | |
IntoIterator<Item = Box<str>> | String | |
IntoIterator<Item = Cow<'a, str>> | String | |
Cow |
IntoIterator<Item = char> | Cow<'a, str> |
IntoIterator<Item = &'b str> | Cow<'a, str> | |
IntoIterator<Item = String> | Cow<'a, str> | |
IntoIterator<Item = T> | Cow<'a, [T] | |
Arc |
IntoIterator<Item = T> | Arc<[T]> |
Vec |
IntoIterator<Item = T> | Vec<T> |
Option |
IntoIterator<Item = Option<A>> | Option<V> |
Result |
IntoIterator<Item = Result<A, E>> | Result<V, E> |
unit |
IntoIterator<Item = ()> | () |
TokenStream |
IntoIterator<Item = TokenTree> | TokenStream |
IntoIterator<Item = TokenStream> | TokenStream | |
HashMap |
IntoIterator<Item = (K, V)> | HashMap<K, V, S> |
HashSet |
IntoIterator<Item = T> | HashSet<T, S> |
OsString |
IntoIterator<Item = OsString> | OsString |
IntoIterator<Item = &'a OsStr> | OsString | |
IntoIterator<Item = Cow<'a, OsStr>> | OsString | |
PathBuf |
IntoIterator<Item = P> | PathBuf |
Wtf8Buf |
IntoIterator<Item = CodePoint> | Wtf8Buf |
根据上表, 我们可以明确判断, 我们的迭代器到底能收集为哪些类型. 同时也可以发现, 当输入的迭代器为IntoIterator<Item = T>
也就是任意类型的迭代器时, 其输出类型是多样的, 如Vec<T>, HashSet<T, S>, BinaryHeap<T>, VecDeque<T>等等. 因此, 对输出类型进行注释很必要.
示例:
let chars = ['g', 'd', 'k', 'k', 'n'];
let hello: String = chars.iter()
.map(|&x| x as u8)
.map(|x| (x + 1) as char)
.collect();
assert_eq!("hello", hello);
用collect
查看是否有Err
产生:
fn main() {
let results = [Ok(1), Err("nope"), Ok(3), Err("bad")];
let result: Result<Vec<_>, &str> = results.iter().cloned().collect();
// gives us the first error
println!("{:?}", result);
// Err("nope")
let results = [Ok(1), Ok(3)];
let result: Result<Vec<_>, &str> = results.iter().cloned().collect();
// gives us the list of answers
println!("{:?}", result);
// Ok([1, 3])
}
为啥有这样的输出呢? 这实际上和 Result
的from_iter()
方法实现有关:
fn from_iter<I: IntoIterator<Item = Result<A, E>>>(iter: I) -> Result<V, E> {
iter::try_process(iter.into_iter(), |i| i.collect())
}
泛型V
是一个可以实现了 FromIterator<A> 的类型, 意思是可以将元素为A
的迭代器转换为V
这种集合类型, try_process()
方法表明了:
Takes each element in the Iterator
: if it is an Err
, no further elements are taken, and the Err
is returned. Should no Err
occur, a container with the values of each Result
is returned.
因此, 如果没有任何Err
产生, 则返回用Ok
包裹的集合V
(如上例的[1, 3]), 只要出现一个Err
立刻结束迭代, 直接返回Err
包裹的信息(如上例的“Nope”).
partition
Consumes an iterator, creating two collections from it.
The predicate passed to partition()
can return true
, or false
. partition()
returns a pair, all of the elements for which it returned true
, and all of the elements for which it returned false
.
See also [is_partitioned()
] and [partition_in_place()
].
拆分迭代器. 将原迭代器消耗, 返回两个新迭代器组成的二元组, 元组左边迭代器为谓词为
true
的元素, 右边为谓词为false
的元素.==传入参数== f: 一个谓词闭包/条件闭包
O(n)复杂度, 没实现二分
fn partition<B, F>(self, f: F) -> (B, B)
where
Self: Sized,
B: Default + Extend<Self::Item>,
F: FnMut(&Self::Item) -> bool,
{
#[inline]
fn extend<'a, T, B: Extend<T>>(
mut f: impl FnMut(&T) -> bool + 'a,
left: &'a mut B,
right: &'a mut B,
) -> impl FnMut((), T) + 'a {
move |(), x| {
if f(&x) {
left.extend_one(x);
} else {
right.extend_one(x);
}
}
}
let mut left: B = Default::default();
let mut right: B = Default::default();
self.fold((), extend(f, &mut left, &mut right));
(left, right)
}
max()
Returns the maximum element of an iterator.
If several elements are equally maximum, the last element is returned. If the iterator is empty, [None
] is returned.
Note that [f32
]/[f64
] doesn't implement [Ord
] due to NaN being incomparable. You can work around this by using [Iterator::reduce
]:
消耗整个迭代器, 返回迭代器中最大元素
如果迭代器所有元素值都相等, 返回最后一个元素, 如果迭代器为空, 返回None
浮点数由于存在
Nan
这个不可比较的值, 所以不能用max
方法. 寻找最大值. 这可以使用reduce
方法解决这个问题
fn max(self) -> Option<Self::Item>
where
Self: Sized,
Self::Item: Ord,
{
self.max_by(Ord::cmp)
}
示例:
下面是用reduce
代替max
的一个示例
fn main() {
assert_eq!(
[2.4, f32::NAN, 1.3]
.into_iter()
.reduce(f32::min)
.unwrap(),
1.3
);
}
min()
Returns the minimum element of an iterator.
返回迭代器中元素最小的值, 和
max
的要求一致
fn min(self) -> Option<Self::Item>
where
Self: Sized,
Self::Item: Ord,
{
self.min_by(Ord::cmp)
}
max_by_key
Returns the element that gives the maximum value from the specified function.
根据给出的指定函数, 判断迭代器的最值
==传入参数== f: 一个闭包, 该闭包返回一个已经实现
Ord
排序特征的类型和
max
直接比较元素大小不同,max_by_key
先是将元素利用给的闭包做一次映射map(f)
, 映射(返回一个二元组, 第一个元素是映射结果, 第二个元素是原值, 做返回值用), 然后再进行max
的比较
fn max_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
where
Self: Sized,
F: FnMut(&Self::Item) -> B,
{
#[inline]
fn key<T, B>(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) {
move |x| (f(&x), x)
}
#[inline]
fn compare<T, B: Ord>((x_p, _): &(B, T), (y_p, _): &(B, T)) -> Ordering {
x_p.cmp(y_p)
}
let (_, x) = self.map(key(f)).max_by(compare)?;
Some(x)
}
max_by
Returns the element that gives the maximum value with respect to the specified comparison function.
用指定地比较函数, 比较迭代器各个元素的值.
这是
max
和max_by_key
的底层方法==传入参数== compare: 一个闭包, 传入两个变量的引用, 输出这两个变量的比较结果
Ordering
当 f 为
|&x, &y| x.cmp(y)
(或者直接写成方法名Ord::cmp
) 时,max_by
就等于max
当 f 为
|&x, &y| |g(x).cmp(g(y)|
(这里g
是一个映射函数, 即max_by_key(g)
方法的传参闭包g
)时,max_by
就等于max_by_key
max_by
更适合自定义最值, 必如如果我要自定义一个二元组, 这样的值时最大的:
- 第一个元素取最大, 第二个元素取最小
那此时就需要用
max_by
要更优雅. (max_by_key
也可以实现, 但不够优雅)详细见示例
fn max_by<F>(self, compare: F) -> Option<Self::Item>
where
Self: Sized,
F: FnMut(&Self::Item, &Self::Item) -> Ordering,
{
#[inline]
fn fold<T>(mut compare: impl FnMut(&T, &T) -> Ordering) -> impl FnMut(T, T) -> T {
move |x, y| cmp::max_by(x, y, &mut compare)
}
self.reduce(fold(compare))
}
源码解释: 从前两个元素开始,利用
compare()
的比较结果对元素逐个比较, 返回较大的元素再与下一个元素比较(reduce
的作用), 直到所有元素比较完成
示例:
取自定义最值:
- 第一个元素取最大, 第二个元素取最小
fn main() {
let sort = [(0, 1), (5, 1), (5, 3), (3, 5)];
// 利用max_by, 实现自定义排序
let max_val = sort.iter().max_by(|&x, &y| {
match x.0.cmp(&y.0) {
std::cmp::Ordering::Equal => y.1.cmp(&x.1),
other => other
}
}).unwrap();
println!("{:?}", max_val);
// 有些数据类型, 也可以使用sort_by_key实现相同功能
// 但和数据类型相关, 比如比较的是usize, 可能代码就又需要改变
let max_key_val = sort.iter().max_by_key(|&x| (x.0, -x.1)).unwrap();
assert_eq!(max_val, max_key_val);
}
min_by_key
Returns the element that gives the minimum value from the specified function.
根据给定函数判断最值
fn min_by_key<B: Ord, F>(self, f: F) -> Option<Self::Item>
where
Self: Sized,
F: FnMut(&Self::Item) -> B,
{
#[inline]
fn key<T, B>(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) {
move |x| (f(&x), x)
}
#[inline]
fn compare<T, B: Ord>((x_p, _): &(B, T), (y_p, _): &(B, T)) -> Ordering {
x_p.cmp(y_p)
}
let (_, x) = self.map(key(f)).min_by(compare)?;
Some(x)
}
min_by
Returns the element that gives the minimum value with respect to the specified comparison function.
自定义比较函数, 实现最小值比较. 比较函数是一个返回
Ordering
枚举的函数详见
max_by
fn min_by<F>(self, compare: F) -> Option<Self::Item>
where
Self: Sized,
F: FnMut(&Self::Item, &Self::Item) -> Ordering,
{
#[inline]
fn fold<T>(mut compare: impl FnMut(&T, &T) -> Ordering) -> impl FnMut(T, T) -> T {
move |x, y| cmp::min_by(x, y, &mut compare)
}
self.reduce(fold(compare))
}
rev()
Reverses an iterator's direction.
翻转迭代器输出顺序. 必须是双端迭代器
DoubleEndedIterator
才可应用所有从左连接的方法均变成了右连接(从右索引)
fn rev(self) -> Rev<Self>
where
Self: Sized + DoubleEndedIterator,
{
Rev::new(self)
}
uzip
Converts an iterator of pairs into a pair of containers.
将一个二元组迭代器转换为两个迭代器
如
[(1, 2), (3, 4), (5, 6)]
–>[1, 3, 5]
和[2, 4, 6]
迭代器的元素必须是二元组
(A, B)
, 可以看出来,rust
的zip
打包解包方法没有python
智能, python 的zip()
可以输入很多迭代器, 组合成一个多元组, 同时zip(*)
又可以把一个多元组/列表等等解包成原来的很多迭代器当然了, 为什么rust没实现呢? 因为不同迭代器的类型可能不同, 没有一种迭代器, 可以让两种不同类型的放到一起(如果有, 那可能是
box
等类型, 但是它就失去了性能), 因此不同类型的迭代器很难实现用同一方法. 但我们可以根据这个方法, 对特定容器自己实现这个功能.
fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)
where
FromA: Default + Extend<A>,
FromB: Default + Extend<B>,
Self: Sized + Iterator<Item = (A, B)>,
{
let mut unzipped: (FromA, FromB) = Default::default();
unzipped.extend(self);
unzipped
}
copied()
Creates an iterator which copies all of its elements.
This is useful when you have an iterator over &T
, but you need an iterator over T
.
用于将引用变为值. 元素必须实现了
Copy
特征底层是利用
Option
的copied()
方法: 通过模式匹配, 把Some(&T)
变为Some(T)
fn copied<'a, T: 'a>(self) -> Copied<Self>
where
Self: Sized + Iterator<Item = &'a T>,
T: Copy,
{
Copied::new(self)
}
cloned()
Creates an iterator which [clone
]s all of its elements.
This is useful when you have an iterator over &T
, but you need an iterator over T
.
There is no guarantee whatsoever about the clone
method actually being called or optimized away. So code should not depend on either.
消耗迭代器的所有元素, 克隆所有元素创建一个新迭代器
这个方法也可以把
&T
变为T
, 但元素可以没实现Copy
特征无法保证
cloned
是被调用执行了还是被底层优化掉了(未执行), 所以所编写的代码应该不要依赖它.
fn cloned<'a, T: 'a>(self) -> Cloned<Self>
where
Self: Sized + Iterator<Item = &'a T>,
T: Clone,
{
Cloned::new(self)
}
示例:
cloned
可能在底层被优化掉, 从而实现错误答案, 因此我们应避免cloned()
这种写法直接生成迭代器, 可能会出问题.
// 下面这个代码是没有出问题的, 只是不好
fn main() {
let a = [1, 2, 3];
let v = a.iter().copied();
// 不要写这种依赖代码
let v_dont = v.clone();
// 最好写这种代码
let v_use = a.iter();
for i in v {
println!("{}", i)
}
for i in v_dont {
println!("{i}")
}
}
cycle()
Repeats an iterator endlessly.
消耗原迭代器, 转换为周期的无限循环迭代器
原来的
None
变成原迭代器的第一个item
, 周而复始
这里还要说明一下, 这个迭代器的
next()
也可能返回None
, 如果原迭代器是空迭代器时, 就返回None
fn cycle(self) -> Cycle<Self>
where
Self: Sized + Clone,
{
Cycle::new(self)
}
sum()
Sums the elements of an iterator.
An empty iterator returns the zero value of the type.
sum()
can be used to sum any type implementing [Sum
] [core::iter::Sum
], including [Option
] and [Result
].
消耗迭代器, 对元素所有元素. 元素要实现了
Sum
特征
fn sum<S>(self) -> S
where
Self: Sized,
S: Sum<Self::Item>,
{
Sum::sum(self)
}
product()
Iterates over the entire iterator, multiplying all the elements
An empty iterator returns the one value of the type.
叠乘迭代器. 消耗迭代器, 返回所有元素相乘的结果
这个方法和
sum
方法都可以用flod
和reduce
实现
fn product<P>(self) -> P
where
Self: Sized,
P: Product<Self::Item>,
{
Product::product(self)
}
示例
// 计算n的阶乘
fn fa(n: i32) -> i32 {
(1..n).product()
}
// 也可以用reduce实现
fn fa2(n: i32) -> i32 {
(1..n).reduce(|a, b| a * b).unwrap_or(1)
}
fn fa2(n: i32) -> i32 {
(1..n).reduce(std::ops::Mul::mul).unwrap_or(1)
}
cmp
Lexicographically compares the elements of this [Iterator
] with those of another.
Lexicographical comparison
Lexicographical comparison is an operation with the following properties:
- Two sequences are compared element by element.
- The first mismatching element defines which sequence is lexicographically less or greater than the other.
- If one sequence is a prefix of another, the shorter sequence is lexicographically less than the other.
- If two sequences have equivalent elements and are of the same length, then the sequences are lexicographically equal.
- An empty sequence is lexicographically less than any non-empty sequence.
- Two empty sequences are lexicographically equal.
按字典序比较两个迭代器大小, 返回比较结果
Ordering
Less
: 左边小于右边,Equal
: 左边等于右边,Greater
: 左边大于右边 A.fn(B) A为左边, B为右边
fn cmp<I>(self, other: I) -> Ordering
where
I: IntoIterator<Item = Self::Item>,
Self::Item: Ord,
Self: Sized,
{
self.cmp_by(other, |x, y| x.cmp(&y))
}
partial_cmp
Lexicographically compares the [PartialOrd
] elements of this [Iterator
] with those of another. The comparison works like short-circuit evaluation, returning a result without comparing the remaining elements. As soon as an order can be determined, the evaluation stops and a result is returned.
partial, 部分的. 对部分有序的类型进行比较大小
按字典序比较两个迭代器大小, 返回比较结果
Option(Ordering)
产生
Option
的原因是, 类型Item
可能存在不能比较大小的值, 因此当与不能比较大小的值比较是, 返回None
fn partial_cmp<I>(self, other: I) -> Option<Ordering>
where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
self.partial_cmp_by(other, |x, y| x.partial_cmp(&y))
}
cmp
和partical_cmp
的区别!
-
cmp 方法实现了
std::cmp::Ord
trait,它用于完全有序(total order)的类型。这意味着所有同类的值都可以相互比较,总是能确定一个值是小于、等于或大于另一个值。cmp
方法返回一个std::cmp::Ordering
枚举值,表示 Less、Equal 或 Greater。 -
partial_cmp 方法实现了
std::cmp::PartialOrd
trait,这个 trait 用于部分有序(partial order)或者说是可能无法比较的所有类型。部分有序意味着某些值可能无法直接比较(例如,NaN(Not a Number)在浮点数中就不与任何值相等,包括自己,也不比任何值大或小)。partial_cmp
返回一个Option<Ordering>
,当两个值可以比较时,它会返回Some(Ordering)
;如果值不能比较(这种情况在实现了PartialOrd
但没有实现Ord
的类型中可能出现),则返回None
。
示例:
use std::cmp::Ordering;
assert_eq!([1.0, f64::NAN].iter().partial_cmp([2.0, f64::NAN].iter()), Some(Ordering::Less
assert_eq!([2.0, f64::NAN].iter().partial_cmp([1.0, f64::NAN].iter()), Some(Ordering::Grea
assert_eq!([f64::NAN, 1.0].iter().partial_cmp([f64::NAN, 2.0].iter()), None);
eq
Determines if the elements of this [Iterator
] are equal to those of another.
消耗迭代器, 判断两个迭代器是否相等
fn eq<I>(self, other: I) -> bool
where
I: IntoIterator,
Self::Item: PartialEq<I::Item>,
Self: Sized,
{
self.eq_by(other, |x, y| x == y)
}
ne
Determines if the elements of this [Iterator
] are not equal to those of another.
判断两个迭代器是否不等
fn ne<I>(self, other: I) -> bool
where
I: IntoIterator,
Self::Item: PartialEq<I::Item>,
Self: Sized,
{
!self.eq(other)
}
lt
Determines if the elements of this [Iterator
] are lexicographically comp less than those of another.
判断左边迭代器是否字典序小于右边迭代器
fn lt<I>(self, other: I) -> bool
where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
self.partial_cmp(other) == Some(Ordering::Less)
}
le
Determines if the elements of this [Iterator
] are lexicographically less or equal to those of another.
判断迭代器是是否小于或等于其他迭代器
fn le<I>(self, other: I) -> bool
where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
matches!(self.partial_cmp(other), Some(Ordering::Less | Ordering::Equal))
}
gt
Determines if the elements of this [Iterator
] are lexicographically greater than those of another.
greater. 左边是否大于右边迭代器
fn gt<I>(self, other: I) -> bool
where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
self.partial_cmp(other) == Some(Ordering::Greater)
}
ge
Determines if the elements of this [Iterator
] are lexicographically greater than or equal to those of another.
greater or equal. 左边是否大于等于右边
fn ge<I>(self, other: I) -> bool
where
I: IntoIterator,
Self::Item: PartialOrd<I::Item>,
Self: Sized,
{
matches!(self.partial_cmp(other), Some(Ordering::Greater | Ordering::Equal))
}
Beta Was this translation helpful? Give feedback.
-
404
https://course.rs/pitfalls/stack-overflow.html
Beta Was this translation helpful? Give feedback.
All reactions