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

6
我希望实现一个算法,能够跳过字符串开头的!或者!^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"));
}

playground

这段代码是可行的,但是我找不到从Peekable迭代器返回到Chars迭代器的方法,所以不得不推进itjt迭代器。这会导致重复的代码。

有没有办法从Peekable迭代器返回到相应的Chars迭代器,或者有更简单的实现算法的方法?

2个回答

6
简而言之,你不能这样做。一般的解决方法是使用类似于 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并且它失败时,底层迭代器已经被提前了。获取剩余的字符串将会失去测试结果为假字符,返回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这样的创建物,它专为在字符串上进行增量式进展而设计。
  • 直接在常规字符串上进行解析,并希望优化器消除冗余边界检查。
  • 使用正则表达式或其他解析库。

只用了5秒钟,甚至都没有收到“新回答”通知。 :P - DK.
1
@DK。那好吧,我就抄袭你回答中我没说的部分。注意我不必加上作者免责声明,这样看起来更正式;-) - Shepmaster
2
免责声明:Shepmaster从strcursor的作者(即我)那里得到了strcursor的建议。它应该更多地被视为一个示例,而不是一种推荐。 - DK.
@DK。boooooooo - Shepmaster

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"));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接