Provide an alternative to scan (which is like a cumulative fold) that works as a cumulative reduce

Hello everyone !

I believe the topic title is pretty explicit. We already have scan, which already allows to implement things like a cumulative sum and so on.

However, when I tried to naively implement the explicit Euler method using iterators, the following code failed to give me the expected result:

let t_end: f64 = 25.0;
let u0: f64 = 1e-5;
let dt: f64 = t_end / (n as f64);

(0..n).scan(u0, |acc, _| {
            *acc = *acc + dt * func(*acc);
            Some(*acc)
}).collect()

The problem here is that the initial value u0 is not kept as the first value of the resulting Vec, and the code "overshoots" by computing one more value than it should.

The solution I found was pretty verbose:

let t_end: f64 = 25.0;
let u0: f64 = 1e-5;
let dt: f64 = t_end / (n as f64);

std::iter::repeat(u0).take(1).chain(
    (1..n).scan(u0, |acc, _| {
        *acc = *acc + dt * func(*acc);
        Some(*acc)
})).collect()

This is kind of ugly in my opinion, and it would be nice if we had a function that gave the exact same result as the above code, but acted as syntactic sugar for it. Something like:

let t_end: f64 = 25.0;
let dt: f64 = t_end / (n as f64);

let u = [0_f64;n];
u[0] = 1e-5;

u.iter().cumulative_reduce(|prev| {
    prev + dt * func(prev)
}).collect()

In the same fashion as reduce, the initial value for prev would be the first element of u, and only subsequent elements would need to be computed using the closure.

Does anyone else think this could be a good idea, or am I the only one ? :grin: Or is there actually already a better way to achieve the same result ?

IMO our Iterator::scan method is a bit weird and confusing, in particular with a background of coming from Haskell.

2 Likes

Try successors:

std::iter::successors(Some(u0), |acc| Some(acc + dt * func(acc)))
        .take(n)
        .collect();
3 Likes

Thanks @quinedot this was exactly what I was looking for. Guess it'll teach me to rely on old ressources that haven't been updated in years: Rust Iterator Cheat Sheet :sweat_smile:

1 Like