2017-04-20 41 views
3

我正試圖在Rust中使用ndarray庫實現一個Conway's Game of Life迭代。突變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 
    } 
} 

此代碼不能編譯!該錯誤位於match塊內,出現「錯誤:無法借用爲可變」和「錯誤:無法分配給不可變索引」。我試圖for &mut window...但庫沒有實現這個(?)

我是比較新的鏽,我相信這可以是由庫的實現窗口的問題。但是,我不確定,也不知道是否有一些變化/修復可以讓我繼續使用這種方法。我是否需要完全廢棄這種方法?我不確定這裏最好的方法是什麼。

任何其他建議或改進的代碼將不勝感激。 (這段代碼沒有實現適當的規則,因爲我在循環,因此我忽略了外部邊緣,但是在這種情況下,這是可以的。另外,做這些事情的任何變化也是可以的 - 細節是不重要的。)

+2

有趣的是,您的康威生活遊戲的方法可能無法正常工作,因爲您在對相鄰單元進行計數時正在逐步突變網格。遊戲假定所有鄰居檢查都是在每個幀中,在任何細胞死亡或重現之前立即執行的。因此,將遊戲的狀態複製到第二個對象中,在您的問題中沒有問題,並且通常會更好。 –

+0

我的確說我沒有執行適當的規則,那是好的 - 主要原因是我努力生成一個與副本一起工作的版本,因爲我無法做一個替代循環來「窗口」 ! – QasimK

+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 
} 
+0

更正:使用'Cell'沒有運行時間開銷(因爲它只是複製底層的值),但是使用'RefCell'(它的引用像一個讀寫互斥體一樣計算)會產生輕微的運行時間開銷。 –