Why does a doubly-reversed iterator act as if it was never reversed?

  • A+
Category:Languages

I have an input vector which contains numbers. In an output vector, I need to get a sequence of partial products but in right-to-left order. The last element of the output must be equal to the last one in the input; the second-to-last element of the output must be a product of the last and second-to-last elements of input; and so on. For example, if the input vector is

let input = vec![2, 3, 4]; 

then I need the output to be [24, 12, 4].

My implementation takes an iterator over the input, reverses it, maps, reverses again and collects:

fn main() {     let input = vec![2, 3, 4];     let mut prod = 1;     let p: Vec<usize> = input         .iter()         .rev()         .map(|v| {             prod *= v;             prod         }).rev()         .collect();     println!("{:?}", p); } 

The result is [2, 6, 24], the same as if I delete both rev()s. The two rev()s do not solve the problem, they just "annihilate" each other.

Is this task solvable in "chain of calls" style, without using for?

 


This behavior is actually explicitly described in the documentation:

Notes about side effects

The map iterator implements DoubleEndedIterator, meaning that you can also map backwards:

[…]

But if your closure has state, iterating backwards may act in a way you do not expect. […]

A way to solve this would be by adding an intermediary collect to be sure that the second rev does not apply on the Map:

fn main() {     let input = vec![2, 3, 4];     let mut prod = 1;     let p: Vec<usize> = input         .iter()         .map(|v| {             prod *= v;             prod         }).rev()         .collect::<Vec<_>>()         .into_iter()         .rev()         .collect();     println!("{:?}", p); } 

But that requires an extra allocation. Another way would be to collect, and then reverse:

fn main() {     let input = vec![2, 3, 4];     let mut prod = 1;     let mut p: Vec<usize> = input         .iter()         .rev()         .map(|v| {             prod *= v;             prod         }).collect();     p.reverse();      println!("{:?}", p); } 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: