改变 Rust ndarray 中的移动窗口

Qas*_*imK 6 multidimensional-array rust

我正在尝试使用ndarray库在 Rust 中实现康威生命游戏的一次迭代。

我认为循环数组的 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
    }
}
Run Code Online (Sandbox Code Playgroud)

这段代码不能编译!错误在match带有“错误:不能借为可变”和“错误:不能分配给不可变索引”的块内。我尝试过,for &mut window...但图书馆没有实现这个(?)

我对 Rust 比较陌生,我相信这可能是图书馆实现 windows 的一个问题。但是,我不确定,也不知道是否有一些变化/修复可以让我继续采用这种方法。我需要完全取消这种方法吗?我不确定这里最好的方法是什么。

对代码的任何其他建议或改进将不胜感激。

(这段代码没有实现正确的规则,因为我在循环时发生变异并且我忽略了外边缘,但是在这种情况下这是可以的。此外,执行这些操作的任何变体也是可以的 - 细节并不重要。 )

Pet*_*all 1

ndarray使用and的一般方法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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我上面所做的实际上只是为了让您的代码正常工作。但是,正如 E_net4 指出的那样,您的解决方案有一个重大错误,因为它在读取时会发生变化。此外,就最佳实践而言,您的使用String并不理想。Anenum更好,因为它更小,可以堆栈分配并且更好地捕获模型的不变量。使用 an ,enum您将得到Copy如下结果,这将允许您使用 aCell代替RefCell,这可能会获得更好的性能,因为它复制数据,而不必计算引用。

#[derive(Debug, PartialEq, Clone, Copy)]
enum CellState {
    Alive,
    Dead
}
Run Code Online (Sandbox Code Playgroud)