简单的实现即可获得输入的每行第一个单词的迭代器

时间:2019-05-18 04:26:46

标签: rust iterator

我需要一个迭代器,该迭代器将流Read实现的每一行的第一个字母单词。此迭代器:

  • 如果读取输入失败,则返回错误
  • 返回字符串的迭代器,每个迭代器代表一个字母单词
  • 忽略空字符串或包含[a-zA-Z]以外的其他字符的第一个单词

我最终完成了以下实现(test here):

fn get_first_words<'a>(r: &'a mut impl Read) -> impl Iterator<Item = Result<String>> + 'a {
    BufReader::new(r).lines().filter_map(|rline| {
        match rline.map(|line| {
            line.split_whitespace()
                .next()
                .filter(|word| word.chars().all(char::is_alphabetic))
                .map(&str::to_string)
        }) {
            Err(e) => Some(Err(e)),
            Ok(Some(w)) => Some(Ok(w)),
            Ok(None) => None,
        }
    })
}

这很好,但是比我预期的要复杂。此实现中有嵌套的迭代器,并且在对包含的值进行过滤时,为了保持Result为包装类型,需要进行一些类型处理。

这可以用更少的嵌套逻辑和更少的类型变体来更简单地编写吗?

1 个答案:

答案 0 :(得分:1)

您可以将match表达式替换为Result::transpose()。我还建议拆分返回第一个单词的函数,以使代码更具可读性。最后,您不需要接受&'a mut impl Read –只需接受impl Read也可以,因为有一个forwarding implementationRead实现了&mut impl Read 。在一起,简化的代码可能看起来像这样:

fn first_word(s: String) -> Option<String> {
    s.split_whitespace()
        .next()
        .filter(|word| word.chars().all(char::is_alphabetic))
        .map(From::from)
}

fn get_first_words(r: impl Read) -> impl Iterator<Item = Result<String>> {
    BufReader::new(r)
        .lines()
        .filter_map(|line| line.map(first_word).transpose())
}

编辑:使用impl Read而不是&mut impl Read会导致可变引用被移到函数中,而不是隐式地重新借用,因此毕竟这不是一个好主意,因为这会使您感到困惑。记得在必要时明确地重新借用它们。