Apa cara pemrograman fungsional dalam mengimplementasikan Conway's Game of Life [ditutup]

12

Saya baru-baru ini menerapkan untuk bersenang-senang Permainan Kehidupan Conway di Javascript (sebenarnya kopi tapi hal yang sama). Karena javascript dapat digunakan sebagai bahasa fungsional, saya mencoba untuk tetap menggunakan spektrum itu. Saya tidak senang dengan hasil saya. Saya seorang programmer OO yang cukup bagus dan solusi saya sama-sama-sama-sama tua. Singkatnya, pertanyaan panjang: apa gaya fungsional (pseudocode) untuk melakukannya?

Inilah Pseudocode untuk usaha saya:

class Node
  update: (board) ->
    get number_of_alive_neighbors from board
    get this_is_alive from board
    if this_is_alive and number_of_alive_neighbors < 2 then die
    if this_is_alive and number_of_alive_neighbors > 3 then die
    if not this_is_alive and number_of_alive_neighbors == 3 then alive

class NodeLocations
  at: (x, y) -> return node value at x,y
  of: (node) -> return x,y of node

class Board
  getNeighbors: (node) -> 
   use node_locations to check 8 neighbors 
   around node and return count

nodes = for 1..100 new Node
state = new NodeState(nodes)
locations = new NodeLocations(nodes)
board = new Board(locations, state)

executeRound:
  state = clone state
  accumulated_changes = for n in nodes n.update(board)
  apply accumulated_changes to state
  board = new Board(locations, state)
George Mauer
sumber
@Ditampilkan yang menyedihkan di atas kepalaku. Saya mengenali konsep-konsep dasar tetapi baru saja
George Mauer
Jauh di atas kepala saya juga ... Saya hanya mempostingnya sebagai contoh tentang apa yang bisa dilakukan oleh seorang ahli bahasa khusus. Sebut saja itu inspirasi bagi kita semua :)
Oded
@ GeorgeMauer "sebenarnya coffeescript tapi hal yang sama" ini adalah hari yang menyedihkan
Raynos

Jawaban:

11

Nah, beberapa ide. Saya bukan ahli FP, tapi ...

Sudah cukup jelas kita harus memiliki tipe Boardyang mewakili kondisi permainan. Dasar dari implementasi harus merupakan evolvefungsi dari tipe evolve :: Board -> Board; artinya menghasilkan Boarddari menerapkan aturan permainan ke a Board.

Bagaimana seharusnya kita menerapkannya evolve? A Boardmungkin harus berupa matriks nxm dari Cells. Kita bisa mengimplementasikan fungsi cellEvolvetipe cellEvolve :: Cell -> [Cell] -> Cellyang diberi a Celldan tetangganya Cellmenghitung Cellstatus pada iterasi berikutnya.

Kita juga harus mengimplementasikan getCellNeighborsfungsi yang mengambil Celltetangga dari Board. Saya tidak sepenuhnya yakin tentang tanda tangan dari metode ini; tergantung pada bagaimana Anda menerapkan Celldan BoardAnda dapat memiliki misalnya getCellNeighbors :: Board -> CoordElem -> CoordElem -> [Cell], yang diberikan Dewan dan dua koordinat ( CoordElemakan menjadi tipe yang digunakan untuk mengindeks posisi dalam Board), memberi Anda daftar panjang variabel tetangga (tidak semua sel di papan memiliki jumlah tetangga yang sama - sudut memiliki 3 tetangga, perbatasan 5 dan yang lainnya, 8).

evolvedengan demikian dapat diimplementasikan dengan menggabungkan cellEvolvedan getCellNeighborsuntuk semua sel di papan, sekali lagi implementasi yang tepat akan tergantung pada bagaimana Anda menerapkan Boarddan Cell, tetapi itu harus menjadi sesuatu seperti "untuk semua sel di papan saat ini, dapatkan tetangga mereka dan menggunakannya untuk menghitung sel yang sesuai papan baru '. Ini harus mungkin dilakukan dengan aplikasi generik dari fungsi-fungsi di seluruh papan menggunakan "peta di atas fungsi sel papan".

Pikiran lain:

  • Anda harus benar-benar mengimplementasikan cellEvolvesehingga dibutuhkan sebagai parameter tipe GameRulesyang mengkodekan aturan permainan- katakanlah daftar tupel (State,[(State,NumberOfNeighbors)],State)yang mengatakan untuk keadaan tertentu dan jumlah tetangga di setiap negara bagian, yang seharusnya menjadi keadaan di iterasi berikutnya . cellEvolveTanda tangan kemudian bisacellEvolve :: GameRules -> Cell -> [Cell] -> Cell

  • Ini secara logis akan membawa Anda untuk evolve :: Board -> Boardberbelok ke evolve :: GameRules -> Board -> Board, sehingga Anda dapat menggunakan evolvetidak berubah dengan berbeda GameRules, tetapi Anda bisa melangkah lebih jauh dan membuat cellEvolvepluggable sebagai gantinya GameRules.

  • Bermain dengan getCellNeighborsAnda juga bisa membuat evolvegenerik berkaitan dengan Boardtopologi - Anda dapat memiliki getCellNeighborsyang membungkus tepi papan, papan 3d, dll.

alex
sumber
9

Jika Anda menulis versi pemrograman fungsional Life, Anda harus mempelajari Algoritma Gosper. Ia menggunakan ide-ide dari pemrograman fungsional untuk mencapai triliunan generasi per detik di papan triliunan kotak di satu sisi. Kedengarannya mustahil saya tahu, tetapi itu sepenuhnya mungkin; Saya memiliki implementasi kecil yang menyenangkan di C # yang dengan mudah menangani papan persegi 2 ^ 64 kotak di samping.

Kuncinya adalah mengambil keuntungan dari kemiripan diri yang besar dari papan-papan Life di kedua waktu dan ruang. Dengan memoize keadaan masa depan bagian besar papan Anda dapat dengan cepat memajukan bagian besar sekaligus.

Saya sudah lama ingin blog pengantar pemula untuk Algoritma Gosper selama bertahun-tahun sekarang, tapi saya tidak pernah punya waktu. Jika saya akhirnya melakukannya, saya akan memposting tautan di sini.

Perhatikan bahwa Anda ingin mencari Algoritma Gosper untuk perhitungan Life , bukan Algoritma Gosper untuk menghitung jumlah hypergeometrik.

Eric Lippert
sumber
terlihat menarik - masih menunggu tautan itu ...;)
jk.
3

Kebetulan bagus, kami membahas masalah yang sebenarnya ini dalam kuliah Haskell kami hari ini. Pertama kali saya melihatnya tetapi di sini ada tautan ke kode sumber yang kami berikan:

http://pastebin.com/K3DCyKj3

Shane
sumber
maukah Anda menjelaskan lebih lanjut tentang apa yang dilakukannya dan mengapa Anda merekomendasikannya untuk menjawab pertanyaan yang diajukan? "Jawaban khusus tautan" tidak diterima di Stack Exchange
agas
3

Anda mungkin ingin melihat implementasinya di RosettaCode untuk mendapatkan inspirasi.

Misalnya ada versi Haskell dan OCaml fungsional yang membuat papan baru setiap belokan dengan menerapkan fungsi ke yang sebelumnya, sedangkan versi grafis OCaml menggunakan dua array dan memperbarui secara bergantian untuk kecepatan.

Beberapa implementasi menguraikan fungsi pembaruan papan menjadi fungsi untuk menghitung lingkungan, menerapkan aturan hidup dan iterasi di atas papan. Itu tampak seperti komponen yang berguna sebagai dasar desain fungsional. Coba ubah hanya papan, jaga yang lainnya sebagai fungsi murni.

Toby Kelsey
sumber
1

Ini versi singkat murni fungsional di Clojure. Semua penghargaan diberikan kepada Christophe Grand yang menerbitkan ini di posting blognya: Permainan Kehidupan Conway

(defn neighbours [[x y]]
  (for [dx [-1 0 1] 
        dy (if (zero? dx) [-1 1] [-1 0 1])]
    [(+ dx x) (+ dy y)]))

(defn step [cells]
  (set (for [[loc n] (frequencies (mapcat neighbours cells))
             :when (or (= n 3) (and (= n 2) (cells loc)))]
         loc)))

Gim kemudian dapat dimainkan dengan berulang kali menerapkan fungsi "langkah" ke sekumpulan sel, misalnya:

(step #{[1 0] [1 1] [1 2]})
=> #{[2 1] [1 1] [0 1]}

Kecerdasannya adalah bagian (sel tetangga mapcat) - yang dilakukan adalah membuat daftar delapan tetangga untuk masing-masing sel aktif dan menyatukan semuanya. Kemudian berapa kali setiap sel muncul dalam daftar ini dapat dihitung dengan (frekuensi ....) dan akhirnya yang memiliki jumlah frekuensi yang tepat berhasil sampai ke generasi berikutnya.

mikera
sumber