Ndarray iterate over nested subviews

Hi! The following code produces the desired result (playground). But I have to use nested .collect()s to avoid the error [E0515]: cannot return value referencing function parameter.

In theory there's some iterator over ArrayViews that would give the same results, but I don't know if it's possible to generate that with .flat_map as I'm attempting.

I found a simpler related question, and the answer seemed to be "don't worry about the .collect(), it's cheap". Perhaps that is the only answer, but I'm hoping to learn of something I have overlooked.

By the way, I think there is a separate problem with the wcopy variable. It's probably best to remove that entirely to simplify this question a bit.

use ndarray::prelude::*;

const GRID: usize = 4;
const WINDOW: usize = 3;

// See https://projecteuler.net/problem=11 for the motivation.
//
// The following produces the desired output, but I don't like that it is
// allocating a Vec<Vec<u32>> for each window, instead of an iterator. In this
// case it's fine, it's only 3×8=24 numbers, but on principle I'd like to
// understand how to indicate that w's lifetime extends as long as that of
// arr.

fn main() {
    let arr = Array::from_shape_vec(
        [GRID, GRID],
        (1..=u32::try_from(GRID * GRID).unwrap()).collect(),
    )
    .unwrap();
    println!("{arr:?}");

    let windows = arr.windows([WINDOW, WINDOW]);
    let window_rows = windows.into_iter().flat_map(|w| {
        // Incidentally, is there a better way to get the reverse diagonal?
        //IGNORE SEPARATE ISSUE: error[E0515]: cannot return value referencing local variable `wcopy`
        //IGNORE let mut wcopy = w.view();
        //IGNORE wcopy.invert_axis(Axis(0));

        // adjacents is Vec<Vec<u32>>, not an iterator
        let adjacents = w
            .rows()
            .into_iter()
            .chain(w.columns().into_iter())
            .chain(std::iter::once(w.diag()))
            //IGNORE .chain(std::iter::once(wcopy.diag()))

            // MAIN QUESTION: Can I avoid one or both of these collects?
            /*.map(|r| r.into_iter().copied().collect::<Vec<_>>())
            .collect::<Vec<_>>()*/;

        adjacents
    });

    for wr in window_rows {
        println!("{wr:?}");
    }
}

Hi, is there a way for me to ask this that might make it easier to help with? Thanks for any pointers on using ndarray efficiently without copying data.

Kind regards,

Tim

You could do your work in a nested loop, effectively.

I didn't see a way to get something like .rows() without borrowing w itself offhand (but I'm not an ndarray expert).

1 Like

Neither a ndarray expert.

The basic error derives from the fact:

fn main() {
    [1; 3].iter().flat_map(|x| Some(x)); // ok: |x: &'any | -> Struct<&'any>
    [1; 3].iter().flat_map(|x| Some(&x)); // fail: |x: &'any | -> Struct<&'another &'any>
}

Another solution giving you the owned result: Rust Playground

1 Like

I'm not sure if ArrayBase::into_owned actually allocates a new vec internally.

But here's another solution of owned Vec<[u32; WINDOW]> by turning each iterator into an array

Thanks both quinedot & vague! It may be that ArrayBase::into_owned() would give what I was looking for.. Moving the work inside the loop is a pragmatic approach, and a good reminder that not everything has to be expressed as iter().map().fold(). :slight_smile:

Tim

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.