6

次の型コンストラクターと関数がどのように機能するかを誰かが説明できますか?

type Rand a = State StdGen a  

getRandom :: (Random a) => Rand a
getRandom = get >>= (\r -> let (a,g) = random r in (put g) >> (return a))

runRand :: Int -> Rand a -> a
runRand n r = evalState r $ mkStdGen n

runRandIO :: Rand a -> IO a
runRandIO r = randomIO >>= (\rnd -> return $ runRand rnd r)

getRandoms :: (Random a) => Int -> Rand [a]
getRandoms n = mapM (\_ -> getRandom) [1..n]
4

2 に答える 2

8

最初から始めましょう:

type Rand a = State StdGen a

この行は、それRand aがタイプのタイプ同義語でありState、その状態がによって与えられStdGen、最終的な値がタイプであることを示していaます。これは、乱数の各要求間で乱数ジェネレーターの状態を格納するために使用されます。

のコードは、getRandomdo表記に変換できます。

getRandom :: (Random a) => Rand a
getRandom = do
  r <- get                   -- get the current state of the generator
  let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen)
    put g                    -- store the new state of the generator
    return a                 -- return the random number that was generated

このrunRand関数は、初期シードとタイプnの値を取ります(これは、覚えておいてください)の同義語です。で新しいジェネレータを作成し、にフィードします。この関数は、状態を無視して、型の戻り値を評価するだけです。rRand aState StdGen amkStdGen nevalState revalStateState s a

ここでも、表記にrunRandIO変換できます。do

runRandIO :: Rand a -> IO a
runRandIO r = do
  rnd <- randomIO        -- generate a new random number using randomIO
  return (runRand rnd r) -- use that number as the initial seed for runRand

最後に、生成するランダム値の数を表す数値をgetRandoms取ります。nリストを作成し、リスト[1..n]に適用getRandomします。の実際の値[1..n]は使用されないことに注意してください(ラムダ関数はで始まるためわかります\_ -> ...)。リストは、正しい数の要素を持つものを持っているだけです。getRandomはモナディック値を返すため、を使用しmapMてリストをマップします。これにより、状態(つまりStdGen)がへの各呼び出しに正しくスレッド化されgetRandomます。

于 2012-06-15T09:14:24.263 に答える
5

基本的な考え方は単純です。疑似乱数を作成するには、関数呼び出しの間に何らかの状態を維持する必要があります。したがって、タイプは「ランダム性に必要な状態とともにRand a」を意味するように定義されます。a

状態はStateモナドを使用して保存されます。これは2つの主要なアクションを提供しますget-とputは、それらがどのように聞こえるかを正確に実行します。したがってgetRandom、現在の状態を調べてからrandom関数を呼び出すだけです。この関数は、ランダム値と新しい状態の2つの値を返します。次に、新しい状態だけをput取得し、結果の値をラップします。

runRandシードを指定して「ランダム」値をアンラップできます。evalState初期状態を指定してステートフル計算(つまり、タイプの値、State s aまたはこの場合はRand a)を実行し、最終状態を破棄して、結果のみを取得できます。したがって、これにより、指定されたシードでを実行しRand a、結果の値のみを返すことができます。値は、同じシードに対して常に同じ結果が得られるためではaなく、タイプだけを持つことができます。Rand a

runRandomIOIOのグローバル状態に基づいてシードを取得することを除いて、同じことを行います。

getRandomsリストのすべての要素をRand a呼び出すことによって(実際の数を無視して)値のリストを取得するだけです。getRandom[1..n]

于 2012-06-15T09:17:41.277 に答える