首页 文章

改变Rust ndarray的旅行窗口

提问于
浏览
4

我试图使用ndarray库在Rust中实现Conway's Game of Life的一次迭代 .

我认为在数组上循环的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
    }
}

这段代码不能编译!该错误位于带有"error: cannot borrow as mutable"和"error: cannot assign to immutable index"的 match 块内 . 我试过 for &mut window... 但是库没有实现这个(?)

我对Rust比较新,我相信这可能是图书馆实施windows的问题 . 但是,我知道是否有一些变化/修复允许我继续这种方法 . 我是否需要完全废弃这种方法?我不确定这里最好的方法是什么 .

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

(这段代码没有实现正确的规则,因为我在循环变异,我忽略了外边缘,但是在这种情况下也没关系 . 另外,做这些事情的任何变化都没关系 - 细节并不重要 . )

1 回答

  • 1

    您使用 ndarraywindows 的一般方法是可以的,但问题是从 windows 迭代器获得的值将始终是不可变的 . 您可以通过将值包装在 CellRefCell 中来解决这个问题,从而为您提供内部可变性 . 也就是说,它们将一个值包装成好像是不可变的,但是提供一个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
    }
    

相关问题