2015-05-25 3 views
1

У меня есть сетка: Vec<Vec<Object>> и пара индексов x/y. Я хочу найти все элементы вокруг тот, который был проиндексирован.Итератор над элементами вокруг определенного индекса в Vec <Vec<Object>>

К сожалению, я не могу просто перебираем элементы, потому что в конечном итоге заимствования Vec дважды и заимствуют шашка кричит на меня:

let mut cells = Vec::with_capacity(8); 

for cx in xstart..xend { 
    for cy in ystart..yend { 
     if cx != x || cy != y { 
      cells.push(&mut squares[cy as usize][cx as usize]); 
     } 
    } 
} 

cells.into_iter() 

Моя лучшая попытка изменения этого в итератора цепи имеет также не удалось эффектно:

let xstart = if x == 0 { x } else { x - 1 }; 
let xlen = if x + 2 > squares[0].len() { x + 1 } else { 3 }; 
let ystart = if y == 0 { y } else { y - 1 }; 
let ylen = if y + 2 > squares.len() { y + 1 } else { 3 }; 

let xrel = x - xstart; 
let yrel = y - ystart; 

squares.iter().enumerate() 
    .skip(ystart).take(ylen).flat_map(|(i, ref row)| 
     row.iter().enumerate() 
      .skip(xstart).take(xlen).filter(|&(j, &c)| i != yrel || j != xrel)) 

Кто-нибудь знает, как я могу это сделать?

+0

Из чистого любопытства вы работаете над игрой жизни Конвея? – Shepmaster

+0

Нет, тральщик :) –

+1

Вы должны обновить свой пример, чтобы стать [MCVE] (http://stackoverflow.com/help/mcve). Поскольку это прямо сейчас, у нас нет значений или типов для многих переменных. – Shepmaster

ответ

2

В конце концов, я сделал пользовательский итератор с помощью ребят в #rust

Я type d моя структура из, чтобы дать вам реальный код. Как отметили ребята в #rust, вы не можете безопасно вернуть &mut из итератора без использования другого итератора, который в любом случае использует unsafe, и учитывая, что математика здесь достаточно проста, чтобы гарантировать, что это не пойдет не так, небезопасным был путь ,

type FieldSquare = u8; 

use std::iter::Iterator; 

pub struct SurroundingSquaresIter<'a> { 
    squares: &'a mut Vec<Vec<FieldSquare>>, 
    center_x: usize, 
    center_y: usize, 
    current_x: usize, 
    current_y: usize, 
} 

pub trait HasSurroundedSquares<'a> { 
    fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a>; 
} 

impl<'a> HasSurroundedSquares<'a> for Vec<Vec<FieldSquare>> { 
    fn surrounding_squares(&'a mut self, x: usize, y:usize) -> SurroundingSquaresIter<'a> { 
     SurroundingSquaresIter { 
      squares: self, 
      center_x: x, 
      center_y: y, 
      current_x: if x == 0 { x } else { x - 1 }, 
      current_y: if y == 0 { y } else { y - 1 }, 
     } 
    } 
} 

impl<'a> Iterator for SurroundingSquaresIter<'a> { 
    type Item = &'a mut FieldSquare; 

    fn next(&mut self) -> Option<&'a mut FieldSquare> { 
     if self.current_y + 1 > self.squares.len() || self.current_y > self.center_y + 1 { 
      return None; 
     } 

     let ret_x = self.current_x; 
     let ret_y = self.current_y; 

     if self.current_x < self.center_x + 1 && self.current_x + 1 < self.squares[self.current_y].len() { 
      self.current_x += 1; 
     } 
     else { 
      self.current_x = if self.center_x == 0 { self.center_x } else { self.center_x - 1 }; 
      self.current_y += 1; 
     } 

     if ret_x == self.center_x && ret_y == self.center_y { 
      return self.next(); 
     } 

     Some(unsafe { &mut *(&mut self.squares[ret_y][ret_x] as *mut _) }) 
    } 
} 
2

Вы хотите получить изменяемые ссылки на все окружающие элементы, не так ли? Я не думаю, что это можно сделать прямо. Проблема в том, что Rust не может статически доказать, что вы хотите изменить ссылки на разные. Если он проигнорировал это, то, например, вы могли бы внести небольшую ошибку в индексирование и получить две изменяемые ссылки на одни и те же данные, что является гарантией Rust для предотвращения. Следовательно, это запрещает это.

На уровне языка это вызвано признаком IndexMut. Вы можете увидеть, как срок служба self параметра своего единственного метода привязан к результату жизни:

fn index_mut(&'a mut self, index: Idx) -> &'a mut Self::Output; 

Это означает, что, если этот метод вызывается (неявно через операцию индексации), то все объект будет заимствован mutably до результирующая ссылка выходит за рамки. Это предотвращает вызов &mut a[i] несколько раз.

Самый простой и безопасный способ исправить это будет реорганизовать ваш код в режиме двойной буферизации - у вас есть два экземпляра поля и копирование данных между собой на каждом шаге. Кроме того, вы можете создать временное поле на каждом шаге и заменить его основным после всех вычислений, но оно, вероятно, менее эффективно, чем обмен двумя полями.

Другой способ решить это, естественно, используя необработанные указатели *mut. Это unsafe и его следует использовать только в качестве последнего средства. Вы можете использовать небезопасность, однако, реализовать безопасную абстракцию, что-то вроде

fn index_multiple_mut<'a, T>(input: &'a mut [Vec<T>], indices: &[(usize, usize)]) -> Vec<&'a mut T> 

где вы сначала проверить, что все индексы различны, а затем использовать unsafe с некоторыми указателями слепков (с transmute, возможно), чтобы создать результирующий вектор ,

Третьим возможным способом было бы использовать метод split_at_mut() каким-то умным способом, но я не уверен, что это возможно, и если это так, это, вероятно, не очень удобно.

+0

Должен ли быть способ сделать это с помощью итераторов? Моя идея состояла в том, чтобы создать цепочку итераторов, которая бы это сделала, но я мог бы написать пользовательский итератор, если это невозможно. –

4

Лично я не уверен, что мне было бы удобно работать с итератором, когда относительные позиции элементов могут быть важны. Вместо этого я хотел бы создать «представление» этих элементов.

gist can be found here, но идея проста, так вот основные конструкции.

#[derive(Debug)] 
struct NeighbourhoodRow<'a, T> 
    where T: 'a 
{ 
    pub left : Option<&'a mut T>, 
    pub center : Option<&'a mut T>, 
    pub right : Option<&'a mut T>, 
} 

#[derive(Debug)] 
struct Neighbourhood<'a, T> 
    where T: 'a 
{ 
    pub top  : NeighbourhoodRow<'a, T>, 
    pub center : NeighbourhoodRow<'a, T>, 
    pub bottom : NeighbourhoodRow<'a, T>, 
} 

Чтобы построить их, я использую здоровую дозу split_at_mut:

fn take_centered_trio<'a, T>(row: &'a mut [T], x: usize) -> 
    (Option<&'a mut T>, Option<&'a mut T>, Option<&'a mut T>) 
{ 
    fn extract<'a, T>(row: &'a mut [T], x: usize) -> (Option<&'a mut T>, &'a mut [T]) { 
     if x+1 > row.len() { 
      (None, row) 
     } else { 
      let (h, t) = row.split_at_mut(x+1); 
      (Some(&mut h[x]), t) 
     } 
    } 

    let (prev, row) = if x > 0 { extract(row, x-1) } else { (None, row) }; 
    let (elem, row) = extract(row, 0); 
    let (next, _) = extract(row, 0); 

    (prev, elem, next) 
} 

, а остальное лишь некоторые неинтересные конструкторами.

Конечно, вы можете построить своего рода итератор через те.

+0

Интересно, что нет способа реализовать метод 'get_mut' для' IterMut', который принимает отрицательные целые числа, тем самым превращая сам IterMut в представление, потому что у IterMut' нет начального указателя. –

Смежные вопросы