[英]How do I replicate Haskell's `scanl (+) 0 xs` in Rust?
If I have an list of numbers [1, 2, 3, 4, 5]
and I wanted to generate a cumulative sum list, in Haskell I would do the following: 如果我有一个数字列表[1, 2, 3, 4, 5]
并且我想生成一个累积和列表,在Haskell中我将执行以下操作:
> let xs = [1, 2, 3, 4, 5]
> scanl (+) 0 xs
[0,1,3,6,10,15]
Trying to get this same behaviour seems unnecessarily troublesome in Rust. 尝试获得相同的行为似乎在Rust中不必要地麻烦。
let xs = [1, 2, 3, 4, 5];
let vs = vec![0]
.into_iter()
.chain(xs.iter().scan(0, |acc, x| {
*acc += x;
Some(*acc)
}))
.collect::<Vec<_>>();
The awkward scan
behaviour of having to mutate the accumulator can be explained by a lack of GC. 必须改变累加器的笨拙scan
行为可以通过缺少GC来解释。 But, scan
also does not include the initial accumulator value, necessitating the need to manually prepend a 0 at the front. 但是, scan
也不包括初始累加器值,因此需要在前面手动预置0。 This itself was troublesome, as I needed to prepend it with chain
and [0].iter()
didn't work, nor did [0].into_iter()
and vec![0].iter()
. 这本身很麻烦,因为我需要在它前面添加chain
而[0].iter()
不起作用,也没有[0].into_iter()
和vec![0].iter()
。 It needed vec![0].into_iter()
. 它需要vec![0].into_iter()
。
I feel like I must be doing something wrong here. 我觉得我必须在这里做错事。 But, what? 但是,什么? Is there a better way to generate a cumulative sum? 有没有更好的方法来生成累积金额? Is it back to a for
loop? 是回到for
循环吗?
Despite the old version of this answer mimics the behavior of scanl
's intermediate form, the execution wasn't lazy. 尽管这个答案的旧版本模仿了scanl
的中间形式的行为,但执行并不是懒惰的。 Updated the generic implementation from my old answer with @French Boiethios's answer . 使用@French Boiethios的答案更新了旧答案中的通用实现。
This is the implementation : 这是实施:
fn scanl<'u, T, F>(op: F, initial: T, list: &'u [T]) -> impl Iterator<Item = T> + 'u
where
F: Fn(&T, &T) -> T + 'u,
{
let mut iter = list.iter();
std::iter::successors(Some(initial), move |acc| iter.next().map(|n| op(n, acc)))
}
//scanl(|x, y| x + y, 0, &[1, 2, 3, 4, 5]).collect::<Vec<_>>()
It can be easily implemented by a fold
它可以通过fold
轻松实现
For an Add
operation: 对于Add
操作:
let result = xs.iter().fold(vec![0], |mut acc, val| {
acc.push(val + acc.last().unwrap());
acc
});
Here is the generic version : 这是通用版本:
fn scanl<T, F>(op: F, initial: T, list: &[T]) -> Vec<T>
where
F: Fn(&T, &T) -> T,
{
let mut acc = Vec::with_capacity(list.len());
acc.push(initial);
list.iter().fold(acc, |mut acc, val| {
acc.push(op(val, acc.last().unwrap()));
acc
})
}
//scanl(|x, y| x + y, 0, &[1, 2, 3, 4, 5])
I would do that with successors
: 我会跟successors
这样做:
fn main() {
let mut xs = vec![1, 2, 3, 4, 5].into_iter();
let vs = std::iter::successors(Some(0), |acc| xs.next().map(|n| n + *acc));
assert_eq!(vs.collect::<Vec<_>>(), [0, 1, 3, 6, 10, 15]);
}
The awkward scan behaviour of having to mutate the accumulator can be explained by a lack of GC. 必须改变累加器的笨拙扫描行为可以通过缺少GC来解释。
There is nothing preventing Rust from doing what you ask. 什么都没有阻止Rust做你所要求的。
Example of possible implementation: 可能的实施示例:
pub struct Mapscan<I, A, F> {
accu: Option<A>,
iter: I,
f: F,
}
impl<I, A, F> Mapscan<I, A, F> {
pub fn new(iter: I, accu: Option<A>, f: F) -> Self {
Self { iter, accu, f }
}
}
impl<I, A, F> Iterator for Mapscan<I, A, F>
where
I: Iterator,
F: FnMut(&A, I::Item) -> Option<A>,
{
type Item = A;
fn next(&mut self) -> Option<Self::Item> {
self.accu.take().map(|accu| {
self.accu = self.iter.next().and_then(|item| (self.f)(&accu, item));
accu
})
}
}
trait IterPlus: Iterator {
fn map_scan<A, F>(self, accu: Option<A>, f: F) -> Mapscan<Self, A, F>
where
Self: Sized,
F: FnMut(&A, Self::Item) -> Option<A>,
{
Mapscan::new(self, accu, f)
}
}
impl<T: ?Sized> IterPlus for T where T: Iterator {}
fn main() {
let xs = [1, 2, 3, 4, 5];
let vs = xs
.iter()
.map_scan(Some(0), |acc, x| Some(acc + x));
assert_eq!(vs.collect::<Vec<_>>(), [0, 1, 3, 6, 10, 15]);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.