如何在迭代时修改集合?

时间:2015-06-21 16:37:43

标签: collections iterator rust immutability

我有一个Board(a.k.a。&mut Vec<Vec<Cell>>)我想在迭代时更新。我想要更新的新值来自一个函数,该函数需要&Vec<Vec<Cell>>到我正在更新的集合。

我尝试了几件事:

  1. 使用board.iter_mut().enumerate()row.iter_mut().enumerate(),以便我可以更新最里面的循环中的cell。 Rust不允许调用next_gen函数,因为它需要&Vec<Vec<Cell>>,并且当你已经有一个可变引用时,你就不能有一个不可变引用。

  2. 更改next_gen功能签名以接受&mut Vec<Vec<Cell>>。 Rust不允许对对象进行多次可变引用。

  3. 我目前将所有更新推迟到HashMap,然后在我执行迭代后应用它们:

    fn step(board: &mut Board) {
        let mut cells_to_update: HashMap<(usize, usize), Cell> = HashMap::new();
        for (row_index, row) in board.iter().enumerate() {
            for (column_index, cell) in row.iter().enumerate() {
                let cell_next = next_gen((row_index, column_index), &board);
                if *cell != cell_next {
                    cells_to_update.insert((row_index, column_index), cell_next);
                }
            }
        }
    
        println!("To Update: {:?}", cells_to_update);
        for ((row_index, column_index), cell) in cells_to_update {
            board[row_index][column_index] = cell;
        }
    }
    

    Full source

    有没有办法可以让代码更新board&#34;就位&#34;,也就是说,在最里面的循环中,同时仍然可以在内部调用next_gen最里面的循环?

    免责声明

    我正在学习Rust,我知道这不是最好的方法。我一直在玩,看看我能做什么,不能做什么。我也试图限制任何复制以限制自己一点点。作为oli_obk - ker mentions, this implementation for Conway's Game of Life is flawed

    此代码旨在衡量一些事项:

    1. 如果可能的话
    2. 如果是惯用的Rust
    3. 根据我在评论中收集的内容,可以使用std::cell::Cell。但是,使用std:cell:Cell规避了一些核心Rust原则,我将其描述为我的困境&#34;在最初的问题中。

2 个答案:

答案 0 :(得分:5)

  

有没有办法让这个代码“就地”更新电路板?

存在专门为这些情况制作的类型。它恰巧被称为std::cell::Cell。你被允许改变class CompassWidget(QWidget): angleChanged = pyqtSignal(float) def __init__(self, parent = None): QWidget.__init__(self, parent) self._angle = 0.0 self._margins = 10 self._pointText = {0: "N", 45: "NE", 90: "E", 135: "SE", 180: "S", 225: "SW", 270: "W", 315: "NW"} self.animation = QPropertyAnimation(self, 'angle') self.animation.setEasingCurve(QEasingCurve.InOutExpo) self.animation.setDuration(1500) def paintEvent(self, event): painter = QPainter() painter.begin(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillRect(event.rect(), self.palette().brush(QPalette.Window)) self.drawMarkings(painter) self.drawNeedle(painter) painter.end() def drawMarkings(self, painter): painter.save() painter.translate(self.width()/2, self.height()/2) scale = min((self.width() - self._margins)/120.0, (self.height() - self._margins)/120.0) painter.scale(scale, scale) font = QFont(self.font()) font.setPixelSize(10) metrics = QFontMetricsF(font) painter.setFont(font) painter.setPen(self.palette().color(QPalette.Shadow)) i = 0 while i < 360: if i % 45 == 0: painter.drawLine(0, -40, 0, -50) painter.drawText(-metrics.width(self._pointText[i])/2.0, -52,self._pointText[i]) else: painter.drawLine(0, -45, 0, -50) painter.rotate(1) i += 1 painter.restore() def drawNeedle(self, painter): painter.save() painter.translate(self.width()/2, self.height()/2) painter.rotate(self._angle) scale = min((self.width() - self._margins)/120.0, (self.height() - self._margins)/120.0) painter.scale(scale, scale) painter.setPen(QPen(Qt.NoPen)) painter.setBrush(self.palette().brush(QPalette.Shadow)) painter.drawPolygon( QPolygon([QPoint(-10, 0), QPoint(0, -45), QPoint(10, 0), QPoint(0, 45), QPoint(-10, 0)]) ) painter.setBrush(self.palette().brush(QPalette.Highlight)) painter.drawPolygon( QPolygon([QPoint(-5, -25), QPoint(0, -45), QPoint(5, -25), QPoint(0, -30), QPoint(-5, -25)]) ) painter.restore() def sizeHint(self): return QSize(600, 600) def angle(self): return self._angle def setAngle(self, angle): if angle != self._angle: self._angle = angle self.update() angle = pyqtProperty(float, angle, setAngle) def animate(self, angle): self.animation.setStartValue(self._angle) self.animation.setEndValue(angle) self.animation.start() if __name__ == "__main__": app = QApplication(sys.argv) window = QWidget() compass = CompassWidget() spinBox = QSpinBox() spinBox.setRange(0, 359) spinBox.editingFinished.connect( lambda: compass.animate(spinBox.value())) layout = QVBoxLayout() layout.addWidget(compass) layout.addWidget(spinBox) window.setLayout(layout) window.show() sys.exit(app.exec_()) 的内容,即使它已被多次不可挽回地借用。 Cell仅限于实施Cell的类型(对于您必须使用RefCell的其他类型),如果涉及多个线程,则必须将Arc与某些类似的组合使用一个Mutex)。

Copy

答案 1 :(得分:5)

这完全取决于您的next_gen功能。假设除了签名之外我们对函数一无所知,最简单的方法是使用索引:

fn step(board: &mut Board) {
    for row_index in 0..board.len() {
        for column_index in 0..board[row_index].len() {
            let cell_next = next_gen((row_index, column_index), &board);
            if board[row_index][column_index] != cell_next {
                board[row_index][column_index] = cell_next;
            }
        }
    }
}

有关next_gen的更多信息,可能有不同的解决方案,但这听起来很像我的细胞自动机,据我所知,这不能用Rust中的迭代器方式完成更改Board的类型。

您可能担心索引解决方案的效率低于迭代器解决方案,但您应该信任LLVM。如果您的next_gen函数位于另一个包中,则应将其标记为#[inline],以便LLVM也可以对其进行优化(如果所有内容都在一个包中,则不需要)。

不是你的问题的答案,而是问题的答案:

由于您正在实施Conway的生命游戏,因此您无法就地进行修改。想象一下以下模式:

00000
00100
00100
00100
00000

如果您更新第2行,它会将该行中的1更改为0,因为其附近只有两个1。这将导致中间1只能看到两个1而不是那里开始的三个Board。因此,您需要复制整个 public void onReceive(Context context, Intent intent) { // Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); if (mp.isPlaying()) { stopPlaying(); mp.stop(); } else { stopPlaying(); mp = MediaPlayer.create(context, R.drawable.aaz); mp.start(); } } private void stopPlaying() { if (mp != null) { mp.stop(); mp.release(); mp = null; }} ,或者像在代码中一样,将所有更改写入其他位置,并在遍历整个板后将其拼接。