我需要一个迭代器来流式传输 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
保持为包装类型。
是否可以将其编写得更简单,使用更少的嵌套逻辑和更少的类型转换?
最佳答案
您可以用 Result::transpose()
替换您的 match
表达式.我还建议拆分返回第一个单词的函数,以使代码更具可读性。最后,您不需要接受 &'a mut impl Read
– 只需接受 impl Read
也可以,因为有一个 forwarding implementation。为 &mut impl Read
实现 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
将导致可变引用被移动到函数中而不是被隐式地重新借用,所以这可能不是一个好主意毕竟,因为记住在必要时明确地重新借用它们会让人感到困惑。
关于rust - 获取每行输入第一个单词的迭代器的简单实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56195957/