我正在尝试使用Conway's Game of Life库在Rust中实现ndarray的一个迭代。
我认为在数组上循环播放3x3窗口是计算居住邻居的一种简单方法,但是我在进行实际更新时遇到了麻烦。
该数组用#
表示生命,而用表示没有生命:
let mut world = Array2::<String>::from_elem((10, 10), " ".to_string());
for mut window in world.windows((3, 3)) {
let count_all = window.fold(0, |count, cell| if cell == "#" { count + 1 } else { count });
let count_neighbours = count_all - if window[(1, 1)] == "#" { 1 } else { 0 };
match count_neighbours {
0 | 1 => window[(1, 1)] = " ".to_string(), // Under-population
2 => {}, // Live if alive
3 => window[(1, 1)] = "#".to_string(), // Re-produce
_ => window[(1, 1)] = " ".to_string(), // Over-population
}
}
此代码无法编译!错误在
match
块中,错误为“错误:不能借用可变变量”和“错误:不能分配给不可变索引”。我尝试了for &mut window...
,但是该库未实现this(?)我对Rust还是比较陌生,我相信这可能与库实现Windows有关。但是,我不确定,也不知道是否存在某些变体/修复程序可以让我继续使用此方法。我需要完全取消这种方法吗?我不确定最好的方法是什么。
任何其他建议或对代码的改进将不胜感激。
(此代码未实现适当的规则,因为我在循环时正在变异,而我忽略了外部边缘,但是在这种情况下还可以。此外,执行这些操作的任何变体也都可以-细节并不重要。 )
最佳答案
使用ndarray
和windows
的一般方法是可以的,但是问题是从windows
迭代器获取的值将始终是不可变的。您可以通过将值包装在Cell
或RefCell
中来解决此问题,这将为您提供内部可变性。也就是说,它们将值包装起来就好像它是不可变的,但是提供了一个API来让您无论如何都要对其进行突变。
这是您的代码,相当残酷地适合使用RefCell
:
use ndarray::Array2;
use std::cell::RefCell;
fn main() {
// creating variables for convenience, so they can be &-referenced
let alive = String::from("#");
let dead = String::from(" ");
let world = Array2::<String>::from_elem((10, 10), " ".to_string());
let world = world.map(RefCell::new);
for mut window in world.windows((3, 3)) {
let count_all = window.fold(0, |count, cell| if *cell.borrow() == &alive { count + 1 } else { count });
let count_neighbours = count_all - if *window[(1, 1)].borrow() == &alive { 1 } else { 0 };
match count_neighbours {
0 | 1 => *window[(1, 1)].borrow_mut() = &dead, // Under-population
2 => {}, // Live if alive
3 => *window[(1, 1)].borrow_mut() = &alive, // Re-produce
_ => *window[(1, 1)].borrow_mut() = &alive, // Over-population
}
}
}
我上面所做的实际上只是使您的代码正常工作。但是,正如E_net4指出的那样,您的解决方案存在一个重大错误,因为它在读取时会发生变化。同样,就最佳实践而言,您对
String
的使用也不理想。 enum
更好,因为它较小,可以堆栈分配,并且可以更好地捕获模型的不变量。使用enum
时,您将按以下方式派生Copy
,这将使您可以使用Cell
而不是RefCell
,这可能会获得更好的性能,因为它可以复制数据,而不必计算引用。#[derive(Debug, PartialEq, Clone, Copy)]
enum CellState {
Alive,
Dead
}
关于multidimensional-array - 使Rust ndarray中的移动窗口发生突变,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43528081/