2010-07-28 30 views
22

Tôi hiện đang là sinh viên đang học về mô hình phản ứng chức năng sử dụng F #. Đó là quan điểm hoàn toàn mới đối với tôi. Hôm qua tôi đã học về việc tạo ra một trò chơi ping-pong đơn giản bằng cách sử dụng mô hình này. Ý tưởng tôi nắm bắt cho đến nay là: chúng tôi nghĩ rằng các giá trị là hàm của thời gian. Trên hình thức thuần khiết, nó không trạng thái. Tuy nhiên, tôi cần nhớ vị trí của quả bóng (hoặc trạng thái). Vì vậy, tôi luôn chuyển vị trí hiện tại của quả bóng thành tham số của hàm toàn cục.Chức năng phản ứng F # - Lưu trữ các trạng thái trong trò chơi

Nếu chúng ta nói về trò chơi phức tạp hơn một chút, giống như Space Invaders, chúng tôi có rất nhiều tiểu bang (vị trí, người ngoài hành tinh người ngoài hành tinh hiện tại HP, số bom còn lại, vv)

Có một tao nhã/tốt nhất cách để giải quyết vấn đề này? Chúng ta có luôn lưu trữ các trạng thái ở cấp cao nhất không? Tất cả các trạng thái hiện tại có nên được đưa ra như là đối số đầu vào bổ sung của hàm toàn cục không?

Ai có thể giải thích điều này bằng cách sử dụng mẫu đơn giản trên F #? Cảm ơn rất nhiều.

Trả lời

13

Có nhiều cách để thực hiện FRP và đó là một lĩnh vực nghiên cứu hoạt động. Điều tốt nhất có thể phụ thuộc rất nhiều vào chi tiết về cách mọi thứ tương tác với nhau và các kỹ thuật mới và tốt hơn có thể xuất hiện trong tương lai.

Nói chung, ý tưởng là có các hành vi có chức năng thời gian thay cho các giá trị thông thường (như bạn đã nói). Hành vi có thể được xác định theo các hành vi khác và có thể được xác định để hoán đổi giữa các hành vi khác khi xảy ra các sự kiện cụ thể.

Trong ví dụ của bạn, bạn thường sẽ không cần nhớ vị trí của quả bóng qua các đối số (nhưng đối với một số loại FRP bạn có thể làm).Thay vào đó, bạn chỉ có thể có hành vi:
ballPos : time -> (float * float)
Điều này có thể có phạm vi toàn cầu hoặc cho một chương trình lớn hơn có thể có phạm vi địa phương hơn với tất cả việc sử dụng phạm vi đó trong phạm vi đó.

Khi mọi thứ trở nên phức tạp hơn, bạn sẽ có hành vi được xác định theo cách ngày càng phức tạp, phụ thuộc vào hành vi và sự kiện khác - bao gồm các phụ thuộc đệ quy được xử lý khác nhau trong các khung FRP khác nhau. Trong F # cho các phụ thuộc đệ quy, tôi mong bạn sẽ cần một let rec bao gồm tất cả các hành vi liên quan. Những vẫn có thể được tổ chức thành các cấu trúc mặc dù - tại cấp cao nhất bạn có thể có:

type alienInfo = { pos : float*float; hp : float } 
type playerInfo = { pos : float*float; bombs : int } 
let rec aliens : time -> alienInfo array =    // You might want laziness here. 
    let behaviours = [| for n in 1..numAliens -> 
         (alienPos player n, alienHP player n) |] 
    fun t -> [| for (posBeh, hpBeh) in behaviours -> 
       {pos=posBeh t; hp=hpBeh t} |]   // You might want laziness here. 
and player : time -> playerInfo = fun t -> 
    { pos=playerPos aliens t; bombs=playerBombs aliens t} 

Và sau đó những hành vi cho alienPos, alienHP thể được định nghĩa, với sự phụ thuộc vào các cầu thủ, và playerPos, playerBombs thể được định nghĩa với phụ thuộc vào người ngoài hành tinh.

Dù sao, nếu bạn có thể cung cấp thêm chi tiết về loại FRP bạn đang sử dụng, sẽ dễ dàng hơn để đưa ra lời khuyên cụ thể hơn. (Và nếu bạn muốn được tư vấn về loại cá nhân nào tôi khuyên bạn nên đọc: http://conal.net/papers/push-pull-frp/push-pull-frp.pdf)

6

Tôi chưa có kinh nghiệm về lập trình phản ứng theo F #, nhưng vấn đề trạng thái toàn cầu trong hệ thống thuần túy là khá phổ biến và có một giải pháp khá thanh lịch: Monads.

Trong khi bản thân monads chủ yếu được sử dụng trong Haskell, khái niệm cơ bản đã biến nó thành F # là computation expressions.

Ý tưởng là bạn không thực sự thay đổi trạng thái nhưng chỉ mô tả các chuyển tiếp của tiểu bang, tức là cách tạo ra các trạng thái mới. Bản thân trạng thái có thể được ẩn hoàn toàn trong chương trình. Bằng cách sử dụng cú pháp monadic đặc biệt, bạn có thể viết các chương trình thuần túy nhưng có trạng thái gần như hoàn toàn bắt buộc.

Lấy một (sửa đổi) thực hiện từ this source, các State đơn nguyên có thể trông như thế này

let (>>=) x f = 
    (fun s0 -> 
     let a,s = x s0  
     f a s)  
let returnS a = (fun s -> a, s) 

type StateBuilder() = 
    member m.Delay(f) = f() 
    member m.Bind(x, f) = x >>= f 
    member m.Return a = returnS a 
    member m.ReturnFrom(f) = f 

let state = new StateBuilder()  

let getState = (fun s -> s, s) 
let setState s = (fun _ ->(),s) 

let runState m s = m s |> fst 

Vì vậy, chúng ta hãy có một ví dụ: Chúng tôi muốn viết một hàm có thể viết giá trị vào một log (chỉ là một danh sách) trong khi tiếp tục. Do đó chúng tôi xác định

let writeLog x = state { 
    let! oldLog = getState // Notice the ! for monadic computations (i.e. where the state is involved) 
    do! setState (oldLog @ [x]) // Set new state 
    return() // Just return(), we only update the state 
} 

Trong state, bây giờ chúng ta có thể sử dụng trong một cú pháp cấp bách mà không cần phải xử lý các danh sách ghi bằng tay.

let test = state { 
    let k = 42 
    do! writeLog k // It's just that - no log list we had to handle explicitly 
    let b = 2 * k 
    do! writeLog b 
    return "Blub" 
} 

let (result, finalState) = test [] // Run the stateful computation (starting with an empty list) 
printfn "Result: %A\nState: %A" result finalState 

Tuy nhiên, mọi thứ đều hoàn toàn chức năng ở đây;)

+0

Đây chủ yếu là về một đơn vị trạng thái. Chức năng lập trình phản ứng thường liên quan đến monads, nhưng thường không phải là loại đơn giản này của nhà nước monad. – RD1

+0

Như tôi đã nói, tôi chưa có kinh nghiệm về FRP. Tuy nhiên, đơn nguyên trạng thái (hoặc monads) dường như là khái niệm đã được yêu cầu - lưu trữ và sửa đổi dữ liệu ngữ cảnh một cách thuận tiện mà không làm mất tính minh bạch tham chiếu. Nếu FTP đã sử dụng một cơ sở hạ tầng đơn điệu, thì càng nhiều càng tốt. Một biến áp đơn vị nhà nước nên làm điều (bạn có nghĩa là với * loại đơn giản *?). Nhưng mà không giải thích nguyên tắc cơ bản, thông tin này sẽ vô dụng! – Dario

+0

Điểm chính của FRP là cho phép các hành vi được định nghĩa là các hàm liên tục của thời gian - ví dụ: bạn có thể xác định vị trí z của một quả bóng dưới lực hấp dẫn như z (t) = 9,8 * t * t. Trạng thái monadic chỉ liên quan đến trạng thái tạo ra những thay đổi rời rạc - những thay đổi rời rạc cũng được cho phép trong FRP, nhưng chúng ít trung tâm hơn và thường chúng không phù hợp với hình thức chính xác của một đơn nguyên. – RD1

3

Tomas đã đưa ra một nice talk về lập trình phản ứng trong F #. Nhiều khái niệm nên áp dụng trong trường hợp của bạn.

+1

Lập trình hoạt động chức năng hơi hơn một chút so với lập trình phản ứng chỉ bằng ngôn ngữ chức năng. Kỹ thuật chính là đại diện cho các hành vi như các hàm của thời gian. Những hành vi này có thể phụ thuộc lẫn nhau và vào các sự kiện. Vì vậy, mà nói chuyện không phải là như vậy có liên quan trực tiếp - nó có một số liên quan, nhưng chỉ vì các sự kiện là một phần của FRP. (Tôi đồng ý đó là một cuộc nói chuyện tốt đẹp mặc dù.) – RD1

0

Có thể bạn sẽ muốn xem FsReactive.

+6

Giải thích cách FsReactive giúp trả lời câu hỏi sẽ cải thiện tính hữu ích của câu trả lời của bạn. –

0

Elm là triển khai FRP hiện đại. Để lập mô hình các bộ sưu tập động có mặt khắp mọi nơi trong các trò chơi như Space Invaders, nó chứa một số Automaton library dựa trên các khái niệm về FRP được mũi tên hóa. Bạn nên kiểm tra nó chắc chắn.

Các vấn đề liên quan