• 迭代器的适配器:peekable的用法
  • 发布于 2个月前
  • 189 热度
    0 评论
peekable 迭代器可以在不产生实际消耗的情况下检查下一个项。调用 Iterator trait 的 peekable 方法即可:
fn peekable(self) -> std::iter::Peekable<Self>
    where Self: Sized;
此处 Peekable<Self> 是一个实现了 Iterator<Item=Self::Item> 的结构体,而 Self 是底层迭代器的类型。Peekable 迭代器有个额外的方法 peek,该方法返回 Option<&Item>:如果底层迭代器已完成迭代,则返回 None,否则返回 Some(r),其中 r 是指向下一个项的共享引用。(注意,如果迭代器返回项的类型已经是对某项的引用,那么最终返回的将是对某个引用的引用)。

调用 peek 会尝试从底层迭代器中抽取下一个项,若存在,则将它缓存起来直到下次调用 next。Peekable 上的其他所有 Iterator 相关方法都知道该缓存的存在:例如 iter.last() 的 iter就知道在耗尽底层迭代器后检查缓存。

当如果不提前窥探就无法决定从一个迭代器中消耗多少项时,peekable 迭代器就显得非常重要。例如,如果要从字符流中解析数字,在数字后面的第一个非数字字符出现之前,是无法确定数字在哪里结束的:
use std::iter::Peekable;

fn parse_number<I>(tokens: &mut Peekable<I>) -> u32
    where I: Iterator<Item=char>
{
    let mut n = 0;
    loop {
        match tokens.peek() {
            Some(r) if r.is_digit(10) => {
                n = n * 10 + r.to_digit(10).unwrap();
            }
            _ => return n
        }
        tokens.next();
    }
}

let mut chars = "226153980,1766319049".chars().peekable();
assert_eq!(parse_number(&mut chars), 226153980);
// 堆代码 duidaima.com
// `parse_number` 还没有消费逗号! 因此可以: 
assert_eq!(chars.next(), Some(','));
assert_eq!(parse_number(&mut chars), 1766319049);
assert_eq!(chars.next(), None);
函数 parse_number 使用 peek 来检查下一个字符,只有当它是数字时才实际消耗它。如果不是数字或迭代器耗尽(即 peek 返回 None),将会返回已解析的数字,并把下一个字符留在迭代器中以备使用。

用户评论