Iterate by not begin at first element

Hello,

I want to start an iteration but not start with the first element. The goal is to browse a large grid, but to have only part of the screen (scroll screen in game)

I hit the synthase used, I saw in the examples (1 ..), but instead has different place it does not work.
In this exemple (x..0)

Thanks in advance,

const ROW: usize = 4;
const COLUMN: usize = 16;

pub struct Map {
    pub grid: [[i32; COLUMN]; ROW],
}


impl Map {
    pub fn new() -> Map {
        Map {
            grid: [
                [0, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 47, 47, 47, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            ],
        }
    }
}

fn main ()
{
    let map = Map::new();
    let ( x ,y ) = ( 0i32 , 0i32 );

    for (r, row) in map.grid.iter().enumerate() {
        print!("{}",r);
        for (c, col) in row.iter().enumerate() {
            println!("{}",c);
            }
        }
}

Iterator::skip/Iterator::take. In your code, something like:

fn main ()
{
    let map = Map::new();
    let ( x_from, x_to, y_from, y_to ) = /* ... */;

    for (r, row) in map.grid.iter().enumerate().skip(x_from).take(x_to - x_from + 1) {
        print!("{}",r);
        for (c, col) in row.iter().enumerate().skip(y_from).take(y_to - y_from + 1) {
            println!("{}",c);
            }
        }
}
1 Like

Thank you,

Nice, that's what I looked for.
I test it in the next days.

Actually after a little rethinking, this may be even more simplified:

fn main ()
{
    let map = Map::new();
    let ( x_from, x_to, y_from, y_to ) = /* ... */;

    for (r, row) in map.grid.iter().enumerate().take(x_to).skip(x_from) {
        print!("{}",r);
        for (c, col) in row.iter().enumerate().take(y_to).skip(y_from){
            println!("{}",c);
            }
        }
}

Those are completelly equivalent. I guess you would find it on your own, but just came to me right now.

Hello,

The solution works well, I just did not understand immediately that r did not start at 0, but has the position in the table.

In the case of very large array, is it better to use this technique

for (r, row) in self.map.grid.iter ().enumerate ().take(x_to).skip(x_from) {...}

or

let slice = & self.map.grid [x_from..x_to];
for (r, row) in slice.iter ().enumerate() {...}

Knowing that there is a refresh of 60 fps

Those two solution are very much equivalent. Possibly the first one may perform one boundary check less, but I don't think it is a thing (firstly because I believe compiler may figure out to optimize it, secondly because this additional bound check is no-time comparing to whole iteration and processing).

However

The first has an advantage of being more generic - it requires, that map.grid is an Iterator and thats all. This may be pros, but it also may be cons. The second solution requres map.grid to Deref to slice, which has guaranteed layout in memory, so it ensures, that the loop is cache-efficient. I don't think, there are any other performance differences (but you would need to profile to be 100% sure).

One think - snippets you gave aren't equivalent. First one is first enumerated, and then cut down, so r would be in range [x_from..x_to). The second one first narrows slice, and then enumerates, so r would be in [0..x_to-x_from). You have to be aware of that. To make them equivalent, you may do:

for (r, row) in self.map.grid.iter ().take(x_to).skip(x_from).enumerate() {...}

or:

let slice = & self.map.grid [x_from..x_to];
for (r, row) in slice.iter ().enumerate() {
  let r = r + x_from;
  // ...
}

whichever range of r is your intention.

Thank you for you help, it's work fine

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.