我想避免实验性功能,所以我在生成器之前考虑其他选项。
我有一个向量 v
由 0 分隔成组。这些分隔组的总和可以通过 for 循环或迭代器累加:
fn main() {
let v = vec![55, 23, 75, 0, 12, 34, 0, 97, 71, 23, 0];
// for loop
let mut acc = 0;
for &vi in &v {
acc += vi;
if vi == 0 {
println!("for yield {}", acc);
acc = 0;
}
}
// iter 'loop'
v.iter()
.scan(0, |acc, &vi| {
*acc += vi;
Some(if vi == 0 {
let total = *acc;
*acc = 0;
Some(total)
} else {
None
})
})
.filter_map(|acc| acc)
.for_each(|acc| println!("iter yield {:?}", acc));
}
scan
用作临时协程,返回 Some(value)
当迭代器产生一个值和 None
当迭代器仍在处理时。 None
被过滤掉并打印总和。上面的例子有点微不足道,因为两个操作产生相同的结果;但是,我的项目需要消化数据流(无法收集到向量中)并有条件地折叠它们(例如,这里的条件可能是
acc
可以被 10 整除,而不是被 0 分隔)。折叠条件本身是流水线式的,这样每个流水线都可以提供一个迭代器(想想嵌套协程)。我想看看是否有替代
Iterator::scan
-> Iterator::filter_map
他们是否使用迭代器。请记住,收集整个数据流是不可能的,因为该流可能是无限的。
最佳答案
for
都没有问题循环或带有 scan()
的版本.如果我正在寻找更清晰的替代方案,我会考虑使用 group_by
来自 itertools
crate .这个版本很优雅,几乎可以满足您的需求:
use itertools::Itertools;
v.iter()
.group_by(|&&vi| vi != 0)
.into_iter()
.map(|(_, group)| group.sum::<u32>())
.for_each(|s| println!("sum {}", s));
问题在于它输出数字 153、0、46、0、191 和 0。0 来自这样一个事实,即它认为元素 0 只是另一个组的键(表达式 vi != 0
的结果)碰巧是假的而不是真的。要解决此问题,您需要更改 map
至 filter_map
与您对 scan()
所做的类似:v.iter()
.group_by(|&&vi| vi != 0)
.into_iter()
.filter_map(|(filt, group)| if filt { Some(group.sum::<u32>()) } else { None })
.for_each(|s| println!("sum {}", s));
不完美,但仍然比原始公式更优雅,因为它使分组明确。
关于rust - Rust 中是否有一个特性允许在没有协程等实验性功能的情况下按组进行折叠?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63910226/