Các cách lập trình chức năng để thực hiện Trò chơi cuộc sống của Conway [đã đóng]


12

Gần đây tôi đã triển khai cho Trò chơi cuộc sống vui nhộn của Conway trong Javascript (thực ra là cà phê nhưng điều tương tự). Vì javascript có thể được sử dụng như một ngôn ngữ chức năng, tôi đã cố gắng duy trì đến cuối phổ. Tôi không hài lòng với kết quả của mình. Tôi là một lập trình viên OO khá giỏi và giải pháp của tôi không giống với người cũ. Vì vậy, câu hỏi dài ngắn: phong cách chức năng (pseudocode) để làm điều đó là gì?

Đây là Pseudocode cho nỗ lực của tôi:

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)


@Oded đó là chán nản trên đầu của tôi. Tôi nhận ra các khái niệm cơ bản nhưng chỉ vừa đủ
George Mauer

Trên đầu tôi cũng vậy ... Tôi chỉ đăng nó như một ví dụ về những gì một bậc thầy về ngôn ngữ chuyên ngành có thể làm. Gọi nó là nguồn cảm hứng cho tất cả chúng ta :)
Oded 20/11/11

@GeorgeMauer "thực sự là coffeescript nhưng điều tương tự" đây là một ngày buồn
Raynos

Câu trả lời:


11

Vâng, một vài ý tưởng. Tôi không phải là chuyên gia về FP, nhưng ...

Rõ ràng là chúng ta nên có một loại Boardđại diện cho trạng thái trò chơi. Cơ sở của việc thực hiện phải là một evolvechức năng của loại evolve :: Board -> Board; nghĩa là nó tạo ra một Boardtừ việc áp dụng các quy tắc của trò chơi vào a Board.

Chúng ta nên thực hiện evolvenhư thế nào? A Boardcó lẽ nên là một ma trận nxm của Cells. Chúng ta có thể thực hiện một hàm cellEvolveloại cellEvolve :: Cell -> [Cell] -> Cellđã cho a Cellvà các Cells lân cận của nó tính toán Celltrạng thái trong lần lặp tiếp theo.

Chúng ta cũng nên thực hiện một getCellNeighborschức năng trích xuất một Cellhàng xóm từ a Board. Tôi không hoàn toàn chắc chắn về chữ ký của phương pháp này; tùy thuộc vào cách bạn triển khai CellBoardví dụ getCellNeighbors :: Board -> CoordElem -> CoordElem -> [Cell], bạn có thể có một bảng và hai tọa độ ( CoordElemsẽ là loại được sử dụng để lập chỉ mục các vị trí trong a Board), cung cấp cho bạn một danh sách độ dài thay đổi của hàng xóm (không phải tất cả các ô trong bảng đều có cùng số lượng hàng xóm - góc có 3 hàng xóm, biên giới 5 và mọi người khác, 8).

evolvedo đó có thể được thực hiện bằng cách kết hợp cellEvolvegetCellNeighborscho tất cả các ô trong bảng, một lần nữa việc thực hiện chính xác sẽ phụ thuộc vào cách bạn thực hiện BoardCell, nhưng nó phải giống như "đối với tất cả các ô trong bảng hiện tại, hãy lấy hàng xóm của chúng và sử dụng chúng để tính toán ô tương ứng của bảng mới '. Điều này có thể được thực hiện với một ứng dụng chung của các chức năng đó trên toàn bộ bảng bằng cách sử dụng "bản đồ trên chức năng tế bào của bảng".

Những suy nghĩ khác:

  • Bạn nên thực sự triển khai cellEvolvesao cho nó là một tham số kiểu GameRulesmã hóa các quy tắc của trò chơi - nói một danh sách các bộ dữ (State,[(State,NumberOfNeighbors)],State)liệu cho một trạng thái nhất định và số lượng lân cận ở mỗi trạng thái, sẽ là trạng thái trong lần lặp tiếp theo . cellEvolveChữ ký của sau đó có thể làcellEvolve :: GameRules -> Cell -> [Cell] -> Cell

  • Điều này sẽ đưa bạn vào evolve :: Board -> Boardbiến một cách hợp lý evolve :: GameRules -> Board -> Board, để bạn có thể sử dụng evolvekhông thay đổi với khác nhau GameRules, nhưng bạn có thể tiến thêm một bước và thực hiện cellEvolvecắm thay vì GameRules.

  • Chơi với getCellNeighborsbạn cũng có thể tạo ra sự evolvechung chung liên quan đến Boardcấu trúc liên kết - bạn có thể có getCellNeighborsbao quanh các cạnh của bảng, bảng 3d, v.v.


9

Nếu bạn đang viết một phiên bản lập trình chức năng của Cuộc sống, bạn nợ chính mình để tìm hiểu về Thuật toán của Gosper. Nó sử dụng các ý tưởng từ lập trình chức năng để đạt được hàng nghìn tỷ thế hệ mỗi giây trên bảng hàng nghìn tỷ hình vuông ở một bên. Điều đó nghe có vẻ không thể tôi biết, nhưng nó hoàn toàn có thể; Tôi có một triển khai nhỏ trong C #, dễ dàng xử lý các ô vuông 2 ^ 64 ô vuông ở một bên.

Bí quyết là tận dụng sự tương đồng lớn của các bảng Life trong cả thời gian và không gian. Bằng cách ghi nhớ trạng thái tương lai của các phần lớn của bảng, bạn có thể nhanh chóng tiến lên các phần lớn cùng một lúc.

Tôi đã có ý định viết blog giới thiệu cho người mới bắt đầu về Thuật toán của Gosper trong nhiều năm nay, nhưng tôi chưa bao giờ có thời gian. Nếu tôi kết thúc như vậy, tôi sẽ đăng một liên kết ở đây.

Lưu ý rằng bạn muốn tìm kiếm Thuật toán của Gosper cho các tính toán của Cuộc sống , chứ không phải Thuật toán của Gosper để tính toán các tổng số siêu âm.


có vẻ thú vị - vẫn đang chờ liên kết đó mặc dù ...;)
jk.

3

Thật trùng hợp, chúng tôi đã đề cập đến vấn đề chính xác này trong bài giảng Haskell của chúng tôi ngày hôm nay. Lần đầu tiên tôi nhìn thấy nó nhưng đây là một liên kết đến mã nguồn mà chúng tôi đã được cung cấp:

http://pastebin.com/K3DCyKj3


bạn có phiền giải thích thêm về những gì nó làm không và tại sao bạn lại đề nghị nó như trả lời câu hỏi được hỏi? "Câu trả lời chỉ liên kết" không được chào đón tại Stack Exchange
gnat

3

Bạn có thể muốn xem các triển khai trên RosettaCode để tìm cảm hứng.

Ví dụ, có các phiên bản Haskell và OCaml chức năng tạo ra một bảng mới mỗi lượt bằng cách áp dụng một chức năng cho lần trước, trong khi phiên bản OCaml đồ họa sử dụng hai mảng và cập nhật xen kẽ cho tốc độ.

Một số triển khai phân tách chức năng cập nhật bảng thành các chức năng để đếm vùng lân cận, áp dụng quy tắc cuộc sốnglặp lại trên bảng. Những thứ đó có vẻ như là các thành phần hữu ích để thiết kế chức năng. Hãy thử sửa đổi chỉ bảng, giữ mọi thứ khác như các chức năng thuần túy.


1

Đây là một phiên bản ngắn hoàn toàn chức năng trong Clojure. Tất cả các khoản tín dụng dành cho Barshe Grand, người đã xuất bản bài này trong bài đăng trên blog của mình: Trò chơi cuộc sống của 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)))

Trò chơi sau đó có thể được chơi bằng cách liên tục áp dụng chức năng "bước" cho một tập hợp các ô, ví dụ:

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

Sự thông minh là phần (các ô lân cận mapcat) - điều này tạo ra một danh sách tám hàng xóm cho mỗi ô đang hoạt động và ghép chúng lại với nhau. Sau đó, số lần mỗi ô xuất hiện trong danh sách này có thể được tính bằng (tần số ....) và cuối cùng là số lần có số lượng tần số phù hợp sẽ chuyển sang thế hệ tiếp theo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.