如何将Peekable迭代器转换回原始迭代器?

时间:2017-04-09 03:11:27

标签: rust

我想实现一个在字符串开头跳过!!^num的算法:

fn extract_common_part(a: &str) -> Option<&str> {
    let mut it = a.chars();
    if it.next() != Some('!') {
        return None;
    }
    let mut jt = it.clone().peekable();

    if jt.peek() == Some(&'^') {
        it.next();
        jt.next();
        while jt.peek().map_or(false, |v| !v.is_whitespace()) {
            it.next();
            jt.next();
        }
        it.next();
    }
    Some(it.as_str())
}

fn main() {
    assert_eq!(extract_common_part("!^4324 1234"), Some("1234"));
    assert_eq!(extract_common_part("!1234"), Some("1234"));
}

这很有效,但我无法找到从Peekable返回Chars的方法,所以我必须提前itjt迭代器。这会导致重复的代码。

如何从Peekable迭代器返回到相应的Chars迭代器,或者可能有更简单的方法来实现此算法?

2 个答案:

答案 0 :(得分:3)

总之,你不能。一般的答案是使用Iterator::by_ref之类的东西来避免使用Chars迭代器:

fn extract_common_part(a: &str) -> Option<&str> {
    let mut it = a.chars();
    if it.next() != Some('!') {
        return None;
    }

    {
        let mut jt = it.by_ref().peekable();

        if jt.peek() == Some(&'^') {
            jt.next();
            while jt.peek().map_or(false, |v| !v.is_whitespace()) {
                jt.next();
            }
        }
    }

    Some(it.as_str())
}

问题在于,当您调用peek并且失败时,底层迭代器已经已经提前。获取字符串的其余部分将丢失测试为false的字符,返回234

但是,Itertools有peeking_take_whiletake_while_ref,两者都可以解决问题。

extern crate itertools;

use itertools::Itertools;

fn extract_common_part(a: &str) -> Option<&str> {
    let mut it = a.chars();
    if it.next() != Some('!') {
        return None;
    }

    if it.peeking_take_while(|&c| c == '^').next() == Some('^') {
        for _ in it.peeking_take_while(|v| !v.is_whitespace()) {}
        for _ in it.peeking_take_while(|v|  v.is_whitespace()) {}
    }

    Some(it.as_str())
}

其他选项包括:

  • 使用像strcursor这样的包,它是为字符串上的这种增量推进而设计的。
  • 直接对常规字符串进行解析,并希望优化器消除冗余边界检查。
  • 使用正则表达式或其他解析库

答案 1 :(得分:1)

如果您只对结果感兴趣,则无需验证:

fn extract_common_part(a: &str) -> Option<&str> {
    a.chars().rev().position(|v| v.is_whitespace() || v == '!')
        .map(|pos| &a[a.len() - pos..])    
}

fn main() {
    assert_eq!(extract_common_part("!^4324 1234"), Some("1234"));
    assert_eq!(extract_common_part("!1234"), Some("1234"));
}