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

后端 未结 2 1994
灰色年华
灰色年华 2021-01-12 02:02

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 m

2条回答
  •  醉话见心
    2021-01-12 02:31

    Your prod variable is carrying state across from one item to the next, which is not what a mapping does. Mappings operate on each element independently, which makes them easily parallelized and easier to reason about. The result you're asking for is to be precise a right scan (a reversed case of a prefix sum), but I'm not sure there are convenient methods to collect from the right (probably the easiest mutable way would be using VecDeque::push_front). This led me to perform the operation in two passes for my first version:

    fn main() {
        let input: Vec = vec![2, 3, 4];
        let initprod = 1;
        let prev: Vec = input
            .iter()
            .rev()
            .scan(initprod, |prod, &v| {
                *prod *= v;
                Some(*prod)
            }).collect();
        let p: Vec = prev.into_iter().rev().collect();
        println!("{:?}", p);
    }
    

    Note that initprod is immutable; prod carries the state. Using into_iter also means prev is consumed. We could use vec.reverse as shown by mcarton, but then we need to have a mutable variable. Scans can be parallelized, but to a lesser degree than maps. See e.g. discussion on adding them to Rayon. One might also consider if a ExactSizeIterator should allow reverse collection into an ordinary vector, but the standard library scan method breaks the known size using Option (which by the next convention turns it into a take-while-scan).

    Here's a fewer copy variant using a preallocated VecDeque to collect from the right. I used an extra scope to restrict the mutability. It also requires Rust 1.21 or later to use for_each. There's unnecessary overhead in tracking the number of items and ring buffer structure, but it's at least somewhat legible still.

    use std::collections::VecDeque;
    
    fn main() {
        let input: Vec = vec![2,3,4];
        let p = {
            let mut pmut = VecDeque::with_capacity(input.len());
            let initprod = 1;
            input
                .iter()
                .rev()
                .scan(initprod, |prod, &v| {
                    *prod *= v;
                    Some(*prod)
                })
                .for_each(|v| { 
                    pmut.push_front(v)
                });
            pmut
        };
        println!("{:?}", p);
    }
    

    Incidentally, following the old adage about Lisp programmers knowing the value of everything and the cost of nothing, here's a Haskell version I don't really know how inefficient it is:

    scanr1 (*) [2, 3, 4]
    

提交回复
热议问题