2012-06-15 8 views
6

Kann jemand beschreiben, wie der folgende Typkonstruktor und die folgenden Funktionen arbeiten?Haskell Zufallsgenerierung

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] 

Antwort

8

Lass uns am Anfang beginnen:

type Rand a = State StdGen a 

Diese Zeile sagt Ihnen, dass Rand a eine Art Synonym für einen Typ State ist, dessen Zustand durch StdGen und deren eventuellen Wert von a Typ gegeben ist. Dies wird verwendet, um den Zustand des Zufallszahlengenerators zwischen jeder Anfrage nach einer Zufallszahl zu speichern.

Der Code für getRandom kann in tun Notation umgewandelt werden:

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 

Die runRand Funktion nimmt eine anfängliche Samen n und einen Wert r von Rand a Typ (die erinnern, ist nur ein Synonym für State StdGen a). Er erstellt einen neuen Generator mit mkStdGen n und führt ihn evalState r zu. Die Funktion evalState wertet nur den Rückgabewert eines State s a Typs aus und ignoriert den Status.

Auch hier können wir runRandIO in do Notation umwandeln:

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 

Schließlich getRandoms nimmt eine Reihe n die Anzahl von Zufallswerten darstellt, die Sie generieren möchten. Es erstellt eine Liste [1..n] und wendet getRandom auf die Liste an. Beachten Sie, dass die tatsächlichen Werte in [1..n] nicht verwendet werden (Sie können erkennen, dass die Lambda-Funktion mit \_ -> ... beginnt). Die Liste ist nur dazu da, etwas mit der richtigen Anzahl von Elementen zu haben. Da getRandom einen monadischen Wert zurückgibt, verwenden wir mapM, um die Liste abzubilden, was dazu führt, dass der Status (d. H. StdGen) korrekt durch jeden der Aufrufe an getRandom gefädelt wird.

5

Die Grundidee ist einfach - um Pseudozufallszahlen zu erstellen, müssen Sie einen Zustand zwischen Funktionsaufrufen beibehalten. Also ist der Typ Rand a definiert als "a zusammen mit dem Zustand, der für die Zufälligkeit benötigt wird".

Der Status wird unter Verwendung der gespeichert. Dies bietet zwei Hauptaktionen - get und put, die genau tun, wie sie klingen. So getRandom sucht nur den aktuellen Zustand und ruft dann die random Funktion auf. Diese Funktion gibt zwei Werte zurück: den Zufallswert und den neuen Status. Dann hast du einfach put den neuen Zustand und wickle den resultierenden Wert ein.

runRand können Sie einen "zufälligen" Wert mit einem Seed auspacken. evalState können Sie eine statusbehaftete Berechnung (dh einen Wert vom Typ State s a oder in diesem Fall Rand a) einen Anfangszustand ausführen und dann nur den endgültigen Zustand verwirft, nur geben Sie das Ergebnis. So können Sie eine Rand a mit einem bestimmten Startwert ausführen und gibt nur den resultierenden Wert zurück. Der Wert kann nur den Typ a anstelle von Rand a haben, da er immer dasselbe Ergebnis für denselben Seed liefert.

runRandomIO macht nur die gleiche Sache, außer erhält den Seed basierend auf einigen globalen Zustand in IO.

getRandoms bekommt man nur eine Liste von Rand a Werte von getRandom für jedes Element der [1..n] Liste aufrufen (die tatsächliche Anzahl ignoriert wird).