2017-04-20 1 views
3

ライブラリを使用してRustにConway's Game of Lifeという1つの反復を実装しようとしています。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...を試みたが、ライブラリはこれを実装していない(?)

私は錆に比較的新しいだと私はこのライブラリによってウィンドウの実装に問題になる可能性がありと信じています。しかし、私は確信していませんし、私はこのアプローチを続けることを可能にするいくつかのバリエーション/修正があるかどうかわかりません。このアプローチを完全に廃止する必要がありますか?私は最良のアプローチがここにあるのかどうかはわかりません。

他の提案や改善があれば幸いです。

(このコードでは、私がループしているときに私は突然変異しているので、適切な規則を実装していませんし、外側の端を無視していますが、この場合は問題ありません。 )

+2

隣接セルをカウントしながら、徐々にグリッドを変異されているので、十分おかしい、ライフゲームのあなたのアプローチは、正常に動作しない場合があります。ゲームでは、すべての隣接チェックが各セルで一度に実行され、すべてのセルが消滅または再現される前に実行されます。したがって、ゲームの状態を第2のオブジェクトに複製することは、あなたの質問には問題がなく、より一般的にはうまくいくでしょう。 –

+0

私は適切なルールを実装していないといいますが、それは大丈夫です。主な理由は、私が "windows"への代替ループを実行できなかったのでコピーで動作するバージョンを作るのに苦労したことでした。 ! – QasimK

+1

あなたが調べるのは良い挑戦のようです。 –

答えて

1

ndarraywindowsを使用する一般的なアプローチは問題ありませんが、問題は、windowsイテレータから取得する値が常に不変であることです。 CellまたはRefCellに値をラップすることで回避できます。内部の変更が可能です。つまり、不変であるかのように値をラップしますが、とにかくそれを変更できるようにAPIを提供します。私は上記のやったことはある

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

本当にちょうどあなたのコードはかなり-あるとして、働いて得るために:ここで

はかなり残酷 RefCellを使用するようになってあなたのコード、です。しかし、E_net4が指摘しているように、あなたのソリューションには大きなバグがあります。また、ベストプラクティスに関しては、 Stringの使用は理想的ではありません。 enumは、サイズが小さく、スタックに割り当てられ、モデルの不変量をよりよく捕捉できるので、はるかに優れています。 enumを指定すると Copyが派生し、 RefCellの代わりに Cellが使用されます。これは、データをコピーするため、参照回数をカウントする必要がないため、パフォーマンスが向上する可能性があります。

#[derive(Debug, PartialEq, Clone, Copy)] 
enum CellState { 
    Alive, 
    Dead 
} 
+0

訂正: 'Cell'を使ってランタイムオーバーヘッドがありません(基本的な値をコピーするだけなので)。しかし、' RefCell'を使ってわずかなランタイムオーバーヘッドがあります(参照は読み取りと書き込みのミューテックスのようにカウントされます)。 –