1

次の計算について説明します。

import Control.Monad.State
import Control.Monad.Identity
import Control.Monad.Random.Class

-- * fair coin
fair :: MonadRandom m => m Bool
fair = (\p -> p <= 0.5) <$> getRandomR (0,1 :: Double)

-- * how do i run this 
bar :: (MonadState Bool m, MonadRandom m) => m Bool
bar = fair >>= \b -> put b >> return b

barそして、予想通り、半分の時間にbar評価されるように実行する方法を知りたいです。True

4

1 に答える 1

3

要するに:

evalRandIO $ evalStateT bar False

これを分けてみましょう。barを状態として維持し、Boolランダムな値を取得できる必要があります。どちらの方法でもネストできます。ここでは、状態をランダムの上に重ねることを選択しましたが、その逆も可能です。

evalStateTこの型シグネチャを持っています:

evalStateT :: Monad m => StateT s m a -> s -> m a

言い換えれば、他のモナドの上にある状態を必要とするものが与えられた場合、それはその状態部分を実装し、基礎となるモナドに関してそのアクションを提供します。名前のeval一部は、結果の状態を破棄し、値だけを提供することを意味します。execStateT結果の状態を提供し、出力をスローするものもあり、runStateT両方のタプルを提供します。いずれにせよ、初期状態を与える必要があることに注意してください。あなたのコードは初期状態を使用していないので、 を使用することもできましたundefinedが、私はそれを使用Falseしました。

状態ビットを実装したので、何が残るでしょうか?

ghci> :t evalStateT bar False
evalStateT bar False :: MonadRandom m => m Bool

ランダムな値を与えることができるモナドが必要です。さて、私たちはそれらの1つを持っています。Randやります。Randも、実際には状態モナドでもあるため、runeval、およびバリアントを持ちます。カバーの下にexecあるタイプのクラスの値を保持します。RandomGenここでは、最後に状態を破棄したくなく、結果も保持したいので、バリアントを使用します。さて、私たちは今何を持っていますか?run

ghci> :t runRand (evalStateT bar False)
runRand (evalStateT bar False) :: RandomGen g => g -> (Bool, g)

比較のために:

ghci> :t random
random :: (RandomGen g, Random a) => g -> (a, g)

これで、乱数ジェネレーターの状態を取得し、結果と新しい状態のペアを吐き出す単純な関数ができましrandomSystem.Random。これをどのように使用できますか?さて、System.Randomモジュールを掘り下げると、getStdRandom役に立ちそうです:

getStdRandom :: (StdGen -> (a, StdGen)) -> IO a

私たちが持っているような関数を取り、それをIOアクションに変えます。(これは、 となるのが理にかなっていますIO。グローバルな状態 (つまり、標準の乱数ジェネレーター) を取得し、それを更新しています。) それを追加した後、何が得られますか?

ghci> :t getStdRandom . runRand $ evalStateT bar False
getStdRandom . runRand $ evalStateT bar False :: IO Bool

まさに私たちが期待するものです: IOa を生成するan Bool. 数回実行します。

ghci> let barIO = getStdRandom . runRand $ evalStateT bar False
ghci> barIO
True
ghci> barIO
True
ghci> barIO
False

私にとっては十分にランダムです。ただし、Carsten がコメントで言及しているように、もっと短い方法があります。これは非常に一般的なユースケースであるため、Control.Monad.Randomモジュールはショートカットを提供します。evalRandIOこれは基本的にgetStdRandom . runRand上記で使用したものです。最終的には簡単な交換です

evalRandIO $ evalStateT bar False
于 2015-12-17T05:40:49.190 に答える