【发布时间】:2018-01-27 10:02:44
【问题描述】:
我有一些实现std::iter::Iterator 的东西,我想知道是否有> 0 元素。这样做的标准方法是什么? count() > 0 看起来太贵了。
我看到了两个候选者:any(|_| true) 和 nth(0).is_some(),但我应该选择哪一个,以便未来的读者可以立即理解我在这里检查的内容?
【问题讨论】:
标签: rust
我有一些实现std::iter::Iterator 的东西,我想知道是否有> 0 元素。这样做的标准方法是什么? count() > 0 看起来太贵了。
我看到了两个候选者:any(|_| true) 和 nth(0).is_some(),但我应该选择哪一个,以便未来的读者可以立即理解我在这里检查的内容?
【问题讨论】:
标签: rust
我会写iter.next().is_some()。
但是,您需要注意这样做会推进迭代器。
fn main() {
let scores = [1, 2, 3];
let mut iter = scores.iter();
println!("{}", iter.next().is_some()); // true
println!("{}", iter.next().is_some()); // true
println!("{}", iter.next().is_some()); // true
println!("{}", iter.next().is_some()); // false
}
在很多情况下我会使用Peekable:
fn main() {
let scores = [1, 2, 3];
let mut iter = scores.iter().peekable();
println!("{}", iter.peek().is_some()); // true
println!("{}", iter.peek().is_some()); // true
println!("{}", iter.peek().is_some()); // true
println!("{}", iter.peek().is_some()); // true
}
让未来的读者一眼就能理解
我将add a method on iterator 命名为is_empty。
【讨论】:
let is_empty = scores.iter().peekable().peek().is_none();.
为Iterator 实现is_empty 的规范方法不是这样做。 Iterator 是 lazy,因此根据定义,如果不进行迭代,它就无法知道它是否还有更多元素。
从逻辑上讲,Iterator 似乎应该很容易知道它是否还有更多元素,但只有在已知其大小的情况下才会出现这种情况(不进行迭代)。事实上,ExactSizeIterator 实现了is_empty。
要检查Iterator 是否为空,您必须尝试迭代并检查是否收到None,但是(如@Shepmaster 所述),您只能在没有推进Iterator 的情况下这样做一个PeekableIterator。您可以在下一个元素处peek() 并检查它是否为is_none():
let mut iterator = vec![1,2,3].into_iter().peekable();
println!("is_empty: {}", iterator.peek().is_none());
考虑到要实现Iterator,这一点变得更加明显,只需提供一个next 函数,该函数返回Some 或None 以指示是否存在更多元素。我们可以提供一个next 的实现,它随机决定下一个元素是否存在,这清楚地表明不执行next 就无法知道我们的迭代器是否为空:
struct RandomThings {}
impl Iterator for RandomThings {
type Item = bool;
fn next(&mut self) -> Option<bool> {
let has_next = rand::random::<bool>();
if has_next {Some(true)} else {None}
}
}
所以创建一个Peekable 迭代器并调用.peek().is_none() 实际上非常明确,以后的读者应该很容易理解。如果您只处理已知大小的迭代器,那么您可以将您的类型进一步限制为ExactSizeIterator 并使用is_empty。如果您要为普通的Iterator 添加is_empty,您只会隐藏必须调用其next 函数以确定它是否为空的事实。
【讨论】: