23

私は現在、F#を使用して関数型リアクティブパラダイムについて学習している学生です。それは私にとって根本的に新しい視点です。昨日、このパラダイムを使用して簡単なピンポンゲームを作成する方法を学びました。私がこれまでに把握している考え方は、価値を時間の関数として考えるというものです。純粋な形では、ステートレスです。ただし、ボールの位置(または状態)を覚えておく必要があります。したがって、私は常にボールの現在の位置をグローバル関数のパラメーターとして渡します。

スペースインベーダーのような少し複雑なゲームについて話すと、多くの状態があります(エイリアンの位置、エイリアンの現在のHP、残りの爆弾の数など)

この問題に取り組むためのエレガントで最良の方法はありますか?常にトップレベルに状態を保存しますか?現在のすべての状態は、グローバル関数の追加の入力引数として指定する必要がありますか?

F#の簡単なサンプルを使用してこれを説明できる人はいますか?どうもありがとう。

4

5 に答える 5

13

FRPを行う方法は複数あり、活発な研究分野です。何が最善かは、物事が互いにどのように相互作用するかの詳細に大きく依存する可能性があり、新しいより良い技術が将来登場する可能性があります。

大まかに言って、(あなたが言ったように)通常の値の代わりに時間の関数である振る舞いをするという考えです。動作は、他の動作の観点から定義でき、特定のイベントが発生したときに他の動作を交換するように定義できます。

あなたの例では、通常、引数を介してボールの位置を覚えておく必要はありません(ただし、FRPの種類によっては覚えておく必要があります)。代わりに、振る舞いをすることができます。
ballPos : time -> (float * float)
これはグローバルスコープを持っている場合があります。または、より大きなプログラムの場合は、そのスコープですべての用途を持つローカルスコープを持っている方がよい場合があります。

物事がより複雑になるにつれて、他の動作やイベントに依存する、ますます複雑な方法で動作が定義されるようになります。これには、さまざまなFRPフレームワークで異なる方法で処理される再帰的な依存関係が含まれます。let rec再帰的な依存関係のF#では、関連するすべての動作を含める必要があると思います。ただし、これらは構造に編成することができます-トップレベルでは、次のようになります。

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}

次に、alienPos、alienHPの動作を、プレーヤーに依存して定義できます。playerPos、playerBombsは、エイリアンに依存して定義できます。

とにかく、使用しているFRPの種類について詳しく教えていただければ、より具体的なアドバイスをするのが簡単になります。(そして、どのような種類のアドバイスが必要な場合は、個人的に読むことをお勧めします:http: //conal.net/papers/push-pull-frp/push-pull-frp.pdf

于 2010-07-28T10:44:41.610 に答える
6

F#でのリアクティブプログラミングの経験はありませんが、純粋関数型システムでのグローバル状態の問題は非常に一般的であり、非常に洗練されたソリューションがあります:モナド

モナド自体は主にHaskellで使用されますが、基本的な概念により、計算式としてF#になりました。

アイデアは、実際に状態を変更するのではなく、状態の遷移、つまり新しい状態を生成する方法を説明するだけであるということです。状態自体は、プログラムで完全に隠すことができます。特別なモナディック構文を使用することにより、純粋でありながらステートフルなプログラムをほぼ必須に作成できます。

このソースから(変更された)実装を取得すると、Stateモナドは次のようになります。

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

例を見てみましょう。続行中に値をログ(リストのみ)に書き込むことができる関数を記述します。したがって、次のように定義します

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
}

stateでは、ログリストを手動で処理しなくても、これを命令構文で使用できるようになりました。

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

それでも、ここではすべてが純粋に機能しています;)

于 2010-07-28T07:49:40.780 に答える
3

Tomasは、F#でのリアクティブプログラミングについて素晴らしい話をしました。多くの概念があなたの場合に当てはまるはずです。

于 2010-07-28T09:55:30.867 に答える
0

たぶん、 FsReactiveを見てみたいと思うでしょう。

于 2011-05-10T18:30:09.113 に答える
0

Elmは最新のFRP実装です。スペースインベーダーなどのゲームに遍在する動的コレクションをモデル化するために、矢印付きFRPの概念に基づくオートマトンライブラリが含まれています。ぜひチェックしてみてください。

于 2013-01-09T18:32:13.107 に答える