Inplace cumulative sum using iterator

I'm trying to make a cumsum vec, but replacing the values inside the vec, so that I can avoid an allocation.

Something like

let a_vec = vec![1.0; 10];
let cumsum: Vec<f64> = a_vec
	.iter()
	.scan(0.0, |acc, &x| {
		*acc += x;
		Some(*acc)
	})
	.collect();

but replacing values.

1 Like

I guess, you could just use a for-loop. E.g.:

let mut a_vec = vec![1.0; 10];
let mut acc = 0.0;
for x in &mut a_vec {
    acc += *x;
    *x = acc;
}
// a_vec now contains cumulative sum(s)
1 Like

I like the for loop, but just for comparison, you could also write it as:

let mut a_vec = vec![1.0; 10];

a_vec.iter_mut().fold(0.0, |acc, x| {
	*x += acc;
	*x
});
1 Like

I knew I could use a for loop. I was looking for a version using iterators.

Thanks.

ACTUALLY... through some use of specialization in the standard library today, especially involving traits such as SourceIter and InPlaceIterable that Scan implements, if you just consume the vector (using into_iter instead of iter), the operation gets compiled as an inplace operation without any extra allocation despite the use of .collect().

I mean, just use into_iter and remove one &

let a_vec = vec![1.0; 10];
let cumsum: Vec<f64> = a_vec
	.into_iter()
	.scan(0.0, |acc, x| {
		*acc += x;
		Some(*acc)
	})
	.collect();

You can use {:p} formatter for printing the addresses of the allocations to see that it’s really inplace

let a_vec = vec![1.0; 10];
println!("{:p}", &a_vec[..]);
let cumsum: Vec<f64> = a_vec
	.into_iter()
	.scan(0.0, |acc, x| {
		*acc += x;
		Some(*acc)
	})
	.collect();
println!("{:p}", &cumsum[..]);
0x560483cf8ad0
0x560483cf8ad0

(playground)

11 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.